Skip to content

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.

  • Entities (the schema source of truth): packages/api/src/**/entities/*.entity.ts
  • Generated migrations: packages/api/src/orm/migrations/ (SQL files plus a meta/ 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.

When you change the schema, the typical loop is:

  1. Edit or add an entity under packages/api/src/**/entities/.

  2. Generate a migration:

    Terminal window
    ./dx db migrate:new

    This runs drizzle-kit generate against the Drizzle config and writes a new numbered SQL file (plus updated snapshot and journal metadata) into packages/api/src/orm/migrations/.

    [!NOTE] Unlike the other db commands, migrate:new runs on the host rather than inside the container.

  3. 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.

  4. Apply the migration:

    Terminal window
    ./dx db migrate

    This runs any pending migrations against the dev database. The apply step also ensures an extensions schema exists and that the vector and pg_trgm PostgreSQL extensions are installed.

  5. Commit the generated migration files together with your entity changes. That includes the new SQL file and the updated files under migrations/meta/.

While iterating locally, you will often want a clean database:

Terminal window
./dx db reset
./dx db reset:seed
./dx db purge

All three commands start from the same schema-cleaning step:

  • ./dx db reset cleans the database, re-runs migrations, and reloads the API
  • ./dx db reset:seed cleans the database, re-runs migrations, re-seeds, and reloads the API
  • ./dx db purge cleans 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 purge is different from ./dx purge db.

  • ./dx db purge cleans schemas inside the running database.
  • ./dx purge db is 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 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.

Terminal window
./dx db seed

Because 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 none to 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.

CommandWhat it does
./dx db migrate:newGenerate a new migration from the current entities (runs on the host)
./dx db migrateApply pending migrations to the dev database
./dx db resetDrop all tables and re-run migrations
./dx db reset:seedDrop all tables, re-migrate, and re-seed
./dx db purgeDrop all app schemas plus the public and drizzle schemas
./dx db seedSeed the database with dev data (idempotent)

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 migrations
  • bun 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.