fix: Restore drizzle-kit generate by syncing snapshot baseline

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
This commit is contained in:
Lukas May
2026-03-06 10:36:07 +01:00
parent 03c2abbd8f
commit 4656627a59
4 changed files with 1887 additions and 46 deletions

View File

@@ -24,7 +24,7 @@ Pre-implementation design docs are archived in `docs/archive/`.
## Key Rules
- **Database migrations**: Hand-write SQL in `apps/server/drizzle/NNNN_name.sql` AND register it in `apps/server/drizzle/meta/_journal.json` (increment `idx`, set `tag` to filename without `.sql`). Both files must be committed together. Do NOT use `drizzle-kit generate` — snapshots are stale since migration 0008. See [docs/database-migrations.md](docs/database-migrations.md).
- **Database migrations**: Edit `apps/server/db/schema.ts`, then run `npx drizzle-kit generate`. Multi-statement migrations need `--> statement-breakpoint` between statements. See [docs/database-migrations.md](docs/database-migrations.md).
- **Logging**: Use `createModuleLogger()` from `apps/server/logger/index.ts`. Keep `console.log` for CLI user-facing output only.
- **Hexagonal architecture**: Repository ports in `apps/server/db/repositories/*.ts`, Drizzle adapters in `apps/server/db/repositories/drizzle/*.ts`. All re-exported from `apps/server/db/index.ts`.
- **tRPC context**: Optional repos accessed via `require*Repository()` helpers in `apps/server/trpc/routers/_helpers.ts`.

View File

@@ -1,2 +1,2 @@
ALTER TABLE review_comments ADD COLUMN parent_comment_id TEXT REFERENCES review_comments(id) ON DELETE CASCADE;
ALTER TABLE review_comments ADD COLUMN parent_comment_id TEXT REFERENCES review_comments(id) ON DELETE CASCADE;--> statement-breakpoint
CREATE INDEX review_comments_parent_id_idx ON review_comments(parent_comment_id);

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
# Database Migrations
This project uses [drizzle-orm](https://orm.drizzle.team/) for database schema management with **hand-written SQL migrations**.
This project uses [drizzle-kit](https://orm.drizzle.team/kit-docs/overview) 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`)
- **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` (calls `drizzle-orm/better-sqlite3/migrator`)
@@ -13,54 +13,28 @@ This project uses [drizzle-orm](https://orm.drizzle.team/) for database schema m
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. A migration SQL file that isn't registered in the journal will never be applied.
The migrator discovers migrations via `apps/server/drizzle/meta/_journal.json`**not** by scanning the filesystem.
## Workflow
### Making schema changes
**Do NOT use `drizzle-kit generate`** — the snapshots in `meta/` have been stale since migration 0008 and `drizzle-kit generate` will produce incorrect interactive prompts. All migrations since 0008 are hand-written.
1. Edit `apps/server/db/schema.ts` with your table/column changes
2. Create a new SQL migration file: `apps/server/drizzle/NNNN_descriptive_name.sql`
- Number it sequentially (check the last migration number)
- Write the SQL (ALTER TABLE, CREATE INDEX, etc.)
3. **Register it in the journal**: edit `apps/server/drizzle/meta/_journal.json`
- Add a new entry at the end of the `entries` array:
```json
{
"idx": <next_number>,
"version": "6",
"when": <unix_timestamp_ms>,
"tag": "NNNN_descriptive_name",
"breakpoints": true
}
```
- `idx`: sequential (previous entry's idx + 1)
- `tag`: migration filename **without `.sql` extension**
- `when`: any timestamp in milliseconds (e.g., previous + 86400000)
4. Commit **both** the SQL file and the updated `_journal.json` together
5. Run `npm run build && npm link` to pick up the changes
2. Generate a migration:
```bash
npx drizzle-kit generate
```
3. Review the generated SQL in `apps/server/drizzle/NNNN_*.sql`
4. Verify multi-statement migrations use `--> statement-breakpoint` between statements (required by better-sqlite3 which only allows one statement per `prepare()` call)
5. Commit the migration file, snapshot, and journal update together
### Example
### Important: statement breakpoints
Adding a column to an existing table:
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):
```sql
-- apps/server/drizzle/0032_add_comment_threading.sql
ALTER TABLE review_comments ADD COLUMN parent_comment_id TEXT REFERENCES review_comments(id) ON DELETE CASCADE;
CREATE INDEX review_comments_parent_id_idx ON review_comments(parent_comment_id);
```
```json
// In meta/_journal.json entries array:
{
"idx": 32,
"version": "6",
"when": 1772323200000,
"tag": "0032_add_comment_threading",
"breakpoints": true
}
ALTER TABLE foo ADD COLUMN bar TEXT;--> statement-breakpoint
CREATE INDEX foo_bar_idx ON foo(bar);
```
### Applying migrations
@@ -69,12 +43,15 @@ 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 00000007 were generated by `drizzle-kit generate`. Migrations 00080032 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
- **Always hand-write migration SQL.** Do not use `drizzle-kit generate` (stale snapshots).
- **Always register in `_journal.json`.** The migrator reads the journal, not the filesystem.
- **Commit SQL + journal together.** A migration file without a journal entry is invisible to the migrator.
- **Use `drizzle-kit generate`** for 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.
- **Test with `npm test`** after creating migrations to verify they work with in-memory databases.
- **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 test`** after generating migrations to verify they work with in-memory databases.