Snapshots were stale since migration 0008. Generated a schema-derived snapshot at 0032 so drizzle-kit generate works again (zero diff on current schema.ts). Also fixed migration 0032 to use statement-breakpoint separator required by better-sqlite3. - Added 0032_snapshot.json derived from current schema.ts - Fixed 0032 SQL to use --> statement-breakpoint between statements - Updated CLAUDE.md and database-migrations.md with correct workflow
3.0 KiB
Database Migrations
This project uses drizzle-kit for database schema management and migrations.
Overview
- Schema definition:
apps/server/db/schema.ts(drizzle-orm table definitions) - Migration output:
apps/server/drizzle/directory (SQL files +meta/_journal.json+meta/NNNN_snapshot.json) - Config:
drizzle.config.ts - Runtime migrator:
apps/server/db/ensure-schema.ts(callsdrizzle-orm/better-sqlite3/migrator)
How It Works
On every server startup, ensureSchema(db) runs all pending migrations from the apps/server/drizzle/ folder. Drizzle tracks applied migrations in a __drizzle_migrations table so only new migrations are applied. This is safe to call repeatedly.
The migrator discovers migrations via apps/server/drizzle/meta/_journal.json — not by scanning the filesystem.
Workflow
Making schema changes
- Edit
apps/server/db/schema.tswith your table/column changes - Generate a migration:
npx drizzle-kit generate - Review the generated SQL in
apps/server/drizzle/NNNN_*.sql - Verify multi-statement migrations use
--> statement-breakpointbetween statements (required by better-sqlite3 which only allows one statement perprepare()call) - Commit the migration file, snapshot, and journal update together
Important: statement breakpoints
better-sqlite3 rejects SQL with multiple statements in a single prepare() call. Drizzle-kit splits on --> statement-breakpoint. If you hand-write or edit a migration with multiple statements, append --> statement-breakpoint to the end of each statement line (before the next statement):
ALTER TABLE foo ADD COLUMN bar TEXT;--> statement-breakpoint
CREATE INDEX foo_bar_idx ON foo(bar);
Applying migrations
Migrations are applied automatically on server startup. No manual step needed.
For tests, the same ensureSchema() function is called on in-memory SQLite databases in apps/server/db/repositories/drizzle/test-helpers.ts.
History
Migrations 0000–0007 were generated by drizzle-kit generate. Migrations 0008–0032 were hand-written (the snapshots fell out of sync). A schema-derived snapshot was restored at 0032, so drizzle-kit generate works normally from that point forward.
Rules
- Use
drizzle-kit generatefor new migrations. It reads schema.ts, diffs against the last snapshot, and generates both SQL + snapshot automatically. - Never use raw CREATE TABLE statements for schema initialization. The migration system handles this.
- Always commit migration files. They are the source of truth for database evolution.
- Migration files are immutable. Once committed, never edit them. Make a new migration instead.
- Keep schema.ts in sync. The schema file is the source of truth for TypeScript types; migrations are the source of truth for database DDL. Both must reflect the same structure.
- Test with
npm testafter generating migrations to verify they work with in-memory databases.