Next-start
database

Database client

Use database client to interact with database

The database client is an instance of the Drizzle client, automatically typed based on the defined schema. It is exposed as the db object and used throughout the application to interact with the database.

This guide covers how to initialize the client and perform basic operations such as querying, creating, updating, and deleting records. For more details, refer to the official Drizzle documentation.

Initializing the clinet

Pass the validated DATABASE_URL to the client to initialize it.

db/index.ts
import { drizzle } from "drizzle-orm/node-postgres";
import "dotenv/config";
 
export const db = drizzle(process.env.DATABASE_URL!);

Now it's exported from the db/index.ts package and can be used across the codebase (server-side).

Querying data

In tRPC procedures, the db instance from Drizzle ORM is used to interact with the database. Since Drizzle provides fully typed queries, it ensures type safety across the backend and frontend when combined with tRPC.

A typical tRPC query procedure retrieves data by calling db inside a resolver function. This allows fetching, filtering, and returning database records efficiently while keeping the API strongly typed.

modules/users/server/procedure.ts
import {eq} from "drizzle-orm";
import {z} from "zod";
 
import { createTRPCRouter } from "@trpc/init";
import {TRPCError} from "@trpc/server";
import {user } from from "@/db/schema"
import {db} from "@/db"
 
export const userRouter = createTRPCRouter({
  getUser: protectedProcedure
    .input(
      z.object({
        id: z.string(),
      }),
    )
    .query(async ({ ctx }) => {
      const { userId: id } = ctx;
 
      if (!id) {
        throw new TRPCError({ code: "UNAUTHORIZED" });
      }
 
      const [userData] = await db.select().from(user).where(eq(user.id, id));
 
      return userData;
    }),
)};

Mutating data

You can use the exported utilities to mutate data. Insert, update or delete records in fast and fully type-safe way:

modules/users/server/procedure.ts
import { z } from "zod";
 
import { createTRPCRouter, publicProcedure } from "@trpc/init";
import { users } from "@/db/schema";
import { db } from "@/db";
 
export const userRouter = createTRPCRouter({
  createUser: publicProcedure
    .input(
      z.object({
        name: z.string(),
        email: z.string().email(),
      })
    )
    .mutation(async ({ input }) => {
      const newUser = await db
        .insert(users)
        .values({
          name: input.name,
          email: input.email,
        })
        .returning();
 
      return newUser[0];
    }),
});

How is this guide?

On this page