Skip to main content

Database migrations

Drizzle ORM manages schema in apps/api/src/db/schema/ and generates migrations via drizzle-kit.

Workflow

Local

# After editing schema
pnpm --filter @gringo-pay/api db:generate

# Apply to your local dev DB (uses NEON_DB_URL from .dev.vars)
pnpm --filter @gringo-pay/api db:migrate

Production

Migrations run as the first step of the API deploy job (.github/workflows/cd.yml):

- name: Run database migrations
run: pnpm --filter @gringo-pay/api db:migrate
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}

The API deploy only proceeds if migrations succeed. Wrangler deploy runs after.

Neon branches for tests

CI uses TEST_NEON_DB_URL — a separate Neon branch DB the tests can reset between runs. Do not point tests at the production branch.

Rules

  • Never edit an already-applied migration. Always generate a new one.
  • Never drop columns or tables without a deprecation window — old Workers versions may still be running during a deploy roll-out.
  • Keep migrations reversible in principle. Drizzle doesn't generate down migrations, but write forwards migrations that could be reversed by a follow-up if needed.
  • Backfills belong in migrations, not in application code. If a new column needs data, backfill it in the same migration that adds it.

Rollback

If a migration is bad but already applied:

  1. Do not try to reverse the migration in place — write a follow-up migration that undoes / fixes it.
  2. If the API deploy is failing because of the new migration, wrangler rollback the API and ship the fix migration.