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
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:generateThis writes a new SQL file to drizzle/. Read it before applying.
Apply it
npm run db:migrateThis 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 generateasks 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 withnpx drizzle-kit generate --custom --name rename-x, write the SQL yourself (ALTER TABLE … RENAME COLUMN …), then check that a plainnpm run db:generatereports 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 studioopens 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.