Database Migrations
Lombok uses Drizzle for its schema and migrations. The database schema is defined by entity files in the API package, and migrations are generated from those entities and committed to the repository.
This page covers the day-to-day migration workflow for contributors. It assumes
you already have a working dev environment. For environment setup, container
startup, and the broader ./dx loop, start with
Local Development with dx.
All ./dx db commands operate against the PostgreSQL instance running inside
the dev container unless noted otherwise.
Where things live
Section titled “Where things live”- Entities (the schema source of truth):
packages/api/src/**/entities/*.entity.ts - Generated migrations:
packages/api/src/orm/migrations/(SQL files plus ameta/folder containing Drizzle’s journal and snapshots) - Drizzle config:
packages/api/src/orm/drizzle.config.ts
Drizzle is configured for the PostgreSQL dialect, reads every *.entity.ts
file under the API src tree, and writes migrations to
packages/api/src/orm/migrations. Database credentials are read from the
standard DB_* environment variables, which are already provided by the dev
container — you do not normally need to set them yourself.
The migration workflow
Section titled “The migration workflow”When you change the schema, the typical loop is:
-
Edit or add an entity under
packages/api/src/**/entities/. -
Generate a migration:
Terminal window ./dx db migrate:newThis runs
drizzle-kit generateagainst the Drizzle config and writes a new numbered SQL file (plus updated snapshot and journal metadata) intopackages/api/src/orm/migrations/.[!NOTE] Unlike the other
dbcommands,migrate:newruns on the host rather than inside the container. -
Review the generated SQL. Drizzle infers changes by diffing your entities against its snapshot. Always read the generated file to confirm it matches your intent, especially for renames, type changes, and destructive operations.
-
Apply the migration:
Terminal window ./dx db migrateThis runs any pending migrations against the dev database. The apply step also ensures an
extensionsschema exists and that thevectorandpg_trgmPostgreSQL extensions are installed. -
Commit the generated migration files together with your entity changes. That includes the new SQL file and the updated files under
migrations/meta/.
Resetting and reseeding
Section titled “Resetting and reseeding”While iterating locally, you will often want a clean database:
./dx db reset./dx db reset:seed./dx db purgeAll three commands start from the same schema-cleaning step:
./dx db resetcleans the database, re-runs migrations, and reloads the API./dx db reset:seedcleans the database, re-runs migrations, re-seeds, and reloads the API./dx db purgecleans the database and stops there
That means purge is useful when you explicitly want to leave the database
without a usable schema until you choose the next step yourself.
[!NOTE]
./dx db purgeis different from./dx purge db.
./dx db purgecleans schemas inside the running database../dx purge dbis a container-lifecycle command that removes the PostgreSQL data volume.Use the former for schema-level cleanup, the latter when you want to discard the database volume entirely.
Seeding
Section titled “Seeding”Seeding is separate from migrations. The default development flow seeds data on
first startup and is idempotent — it records that it has already run using a
_dev_flags table, so running it again is a no-op.
./dx db seedBecause seeding is idempotent, the usual way to re-seed is to reset first with
./dx db reset:seed, or run ./dx db seed after ./dx db reset.
Which seed runs is controlled by the DEV_SEED_FILE environment variable:
- defaults to
default.ts - set it to
noneto skip seeding - point it at a custom file to use your own seed
Seed files live in packages/api/script/db-seeds/. To create a local seed that
git ignores, copy default.ts to local-<anything>.ts in that directory and
set DEV_SEED_FILE=local-<anything>.ts.
Command reference
Section titled “Command reference”| Command | What it does |
|---|---|
./dx db migrate:new | Generate a new migration from the current entities (runs on the host) |
./dx db migrate | Apply pending migrations to the dev database |
./dx db reset | Drop all tables and re-run migrations |
./dx db reset:seed | Drop all tables, re-migrate, and re-seed |
./dx db purge | Drop all app schemas plus the public and drizzle schemas |
./dx db seed | Seed the database with dev data (idempotent) |
Advanced commands
Section titled “Advanced commands”The API package also defines lower-level Drizzle scripts that are not wired into
./dx. They are run directly with Bun from packages/api and are mainly
useful for debugging migration state:
bun db:migrate:check— run Drizzle’s consistency check on the migrationsbun db:migrate:drop— drop a migration via Drizzle’s interactive tooling
Use these with care, and prefer the ./dx db commands above for everyday work.
Because they are not part of the standard dx workflow, verify their current
behavior in packages/api/package.json before relying on them.