Skip to main content

Database

Drizzle, over pg: any Postgres.

Neon on Vercel, a container, a managed instance. Tables are prefixed id_, so one database can serve a whole deployment.

Locally

With no database URL, the service starts PGlite⁠ (external site)(pglite.dev) — Postgres compiled to WebAssembly — in .data/id, behind a local Postgres socket on port 54310, so the app still talks to it through pg. PGlite would be corrupted by two processes, so whoever holds its lock file serves it, and the CLI or drizzle-kit studio connect to that port. It is migrated for you.

Migrations

The schema is lib/server/schema.ts. drizzle-kit writes each migration forward; its rollback is the hand-written <tag>.down.sql beside it.

pnpm db:generate --name add-thing    # the next migration, and a stub to undo it
pnpm db:migrate
pnpm db:rollback [--steps N | --to TAG]
pnpm db:status

Migrations run in the order of drizzle/meta/_journal.json, each in its own transaction, under a lock, and each is recorded with a hash: one edited after it ran is refused, as is running against a database that has migrations this version does not know. A migration without a written down file cannot be rolled back, and says so rather than half-trying.

A real database is migrated by the build that deploys it, after next build — pnpm migrate, which is fg-dist migrate --build — or by hand with pnpm db:migrate. Never at start-up, so a deployment never changes its schema by surprise and a rollback is not undone by the next cold start. A Vercel rollback promotes an older deployment without building, so every migration has to work with the release before it as well: add a column before the code that needs it, and drop one a release after the code stops using it.

Where the migrations are, the journal table they are recorded in (id_migrations) and the lock they run under are declared in package.json, under fairgarden.migrations. That is what lets a monolith migrate this app without loading it. The migrator itself is fg-dist's, the same one the embedded database is migrated by.