Skip to Content
LaunchfastDocs

Database and migrations

Add tables and columns with Drizzle, create migrations and apply them safely.

The schema is defined in TypeScript in src/db/schema.ts. Drizzle turns changes into SQL migrations in drizzle/.

Change the schema

Edit the schema

src/db/schema.ts
export const project = pgTable('project', {
  id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
  userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
  name: text('name').notNull(),
  createdAt: timestamp('created_at').notNull().defaultNow(),
});

Generate a migration

npm run db:generate

This writes a new SQL file to drizzle/. Read it before applying.

Apply it

npm run db:migrate

This applies migrations to the database in DATABASE_URL. If production uses a different database, run it against that one too, before deploying code that needs the change.

Commit both

Commit src/db/schema.ts and the new files in drizzle/ (including drizzle/meta/).

Querying

import { eq } from 'drizzle-orm';
import { db } from '@/db';
import { project } from '@/db/schema';

const projects = await db.query.project.findMany({ where: eq(project.userId, user.id) });
await db.insert(project).values({ userId: user.id, name: 'My project' });

The kit uses Neon's HTTP driver: each query is one HTTP request, which suits serverless. It doesn't support interactive transactions (db.transaction(…)). Use db.batch([...]) to run several statements atomically.

Rules

Never edit an applied migration

Once a migration has run on any database, don't change or delete its file. Drizzle won't run it again, so your databases drift apart. Change src/db/schema.ts and generate a new migration instead.

  • Renames: drizzle-kit generate asks interactive questions when it can't tell a rename from a drop and add. Those fail in non-interactive terminals (including AI agents). Create an empty migration with npx drizzle-kit generate --custom --name rename-x, write the SQL yourself (ALTER TABLE … RENAME COLUMN …), then check that a plain npm run db:generate reports no changes.
  • Auth tables (user, session, account, verification) use the field names Better Auth expects. Add columns if you need to, but don't rename existing ones.
  • Money is stored as integers in the currency's smallest unit (cents, paise).

Looking at your data

  • npx drizzle-kit studio opens a local browser UI for your tables.
  • Or use the SQL Editor and Tables pages in the Neon console.

See Database schema for every table.

On this page