Skip to main content
General

Database

Learn how to manage your database, schema, and migrations with Drizzle ORM.

Open MarkdownFull AI corpusFeedback

The Pro Next.js Drizzle starter kit uses Drizzle ORM with PostgreSQL. Drizzle provides a lightweight, fully type-safe way to interact with your database.

Client Setup

The database client is initialized in lib/db/client.ts and exported from lib/db/index.ts. It uses the postgres driver for high performance.

lib/db/client.ts
import { drizzle } from 'drizzle-orm/node-postgres';

import { env } from '@/lib/env';

import * as schema from './schema';

export const db = drizzle(env.DATABASE_URL, {
  schema
});

The client is then exported from lib/db/index.ts:

lib/db/index.ts
export * from './client';
export * from './schema';

Schema Definition

Your database schema is defined in lib/db/schema/. We recommend splitting your schema into multiple files for better organization:

  • tables.ts: Table definitions.
  • enums.ts: Enum definitions.
  • relations.ts: Relation definitions.

Example Table Definition

lib/db/schema/tables.ts
import {
  boolean,
  index,
  pgTable,
  text,
  timestamp,
  uuid
} from 'drizzle-orm/pg-core';

import { organizationTable } from './organization';

export const leadTable = pgTable(
  'lead',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    organizationId: uuid('organization_id')
      .notNull()
      .references(() => organizationTable.id, { onDelete: 'cascade' }),
    firstName: text('first_name').notNull(),
    lastName: text('last_name').notNull(),
    email: text('email').notNull(),
    createdAt: timestamp('created_at', { withTimezone: true })
      .notNull()
      .defaultNow()
  },
  (table) => [index('lead_organization_id_idx').on(table.organizationId)]
);

Migrations

Commands

CommandDescription
npm run db:generateGenerate migration from schema changes
npm run db:migrateApply pending migrations
npm run db:studioOpen Drizzle Studio GUI
npm run db:pushPush schema directly (dev only, no migration)

Migration Workflow

  1. Edit schema in lib/db/schema/tables.ts.
  2. Generate migration: npm run db:generate.
  3. Review migration in lib/db/migrations/.
  4. Apply migration: npm run db:migrate.

Multi-Tenancy

Critical: Always filter by organizationId for tenant data to ensure data isolation.

trpc/routers/lead-router.ts
const leads = await db.query.leadTable.findMany({
  where: eq(leadTable.organizationId, ctx.organization.id)
});

Transactions

Use transactions for related operations that must be atomic.

lib/actions/widget.ts
const result = await db.transaction(async (tx) => {
  await tx
    .delete(subscriptionItemTable)
    .where(eq(subscriptionItemTable.subscriptionId, subId));

  const items = await tx
    .insert(subscriptionItemTable)
    .values(newItems)
    .returning();

  return items;
});