Skip to content

Migration

Mantle uses Drizzle Kit to generate migration SQL files and a custom runner in @mantleframework/database to apply them — with full Aurora DSQL constraint handling built in.

Final result

bash
# Edit schema, generate, review, apply
mantle db generate
mantle db migrate

Step 1: Edit your schema

Define tables in src/entities/schema.ts using Drizzle's schema DSL:

typescript
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core'

export const items = pgTable('items', {
  id: text('id').primaryKey(),
  name: text('name').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
})

Use text primary keys with gen_random_uuid() defaults on Aurora DSQL — no serial or sequences.

Step 2: Generate a migration

bash
mantle db generate

Wraps npx drizzle-kit generate. Compares the current schema against migration history and writes a new .sql file to migrations/. Requires an interactive terminal — run in a real terminal session, not CI.

Step 3: Check DSQL compatibility

bash
mantle db check-dsql

Analyzes migration files before applying. Output:

0001_initial.sql
  [1] OK     CREATE TABLE "items" (...)
  [2] INDEX  CREATE INDEX "items_name_idx" ON ...  (will use CREATE INDEX ASYNC)
  [3] STRIP  ALTER TABLE ... ENABLE ROW LEVEL SECURITY  (not supported by DSQL)

Summary:
  Total: 3  |  Compatible: 1  |  Index (ASYNC): 1  |  Stripped: 1
ClassificationMeaning
OKFully compatible
INDEXRewritten to CREATE INDEX ASYNC at runtime
STRIPUnsupported — silently skipped (RLS, DO blocks, CREATE SEQUENCE)
RECREATIONALTER TABLE requiring column-type change — runner recreates the table

Step 4: Apply migrations

bash
mantle db migrate

Tracks applied migrations by SHA-256 hash (not filename). Acquires an advisory lock in __mantle_migration_lock to prevent concurrent runs. The lock expires after 5 minutes to handle crashed processes.

Options

FlagDefaultDescription
--folder <path>./migrationsMigrations directory
--provider <type>auto-detectaurora-dsql, aurora-serverless-v2, or neon
--endpoint <url>DSQL_ENDPOINT envAurora DSQL cluster endpoint
--connection-string <url>DATABASE_URL envConnection string (Neon / Aurora v2)
--region <region>AWS_REGION envAWS region
--no-lockSkip migration lock (not recommended in production)

Provider is inferred automatically: DSQL_ENDPOINT set → aurora-dsql, DATABASE_URL set → neon.

Aurora DSQL constraints

ConstraintHow the runner handles it
No SERIAL / sequencesUse gen_random_uuid() for PKs
No synchronous index creationCREATE INDEXCREATE INDEX ASYNC
No Row Level SecurityENABLE ROW LEVEL SECURITY stripped
No PL/pgSQL DO blocksStripped silently
One DDL per transactionEach statement runs in its own transaction

Idempotent retry

The runner catches PostgreSQL errors that indicate a statement already applied:

CodeMeaning
42P07Table already exists
42P01Table does not exist (DROP on missing)
42701Column already exists
42710Index or constraint already exists

Table recreation

ALTER TABLE operations that change column types are not supported by DSQL. The runner detects these and executes a four-step recreation: create new table → copy rows → drop old table → rename. Table recreation is not wrapped in an idempotent catch — if it fails partway, fix the underlying issue and re-run mantle db migrate.

MigrateDSQL Lambda

Projects using Aurora DSQL include a standalone Lambda at src/lambdas/standalone/MigrateDSQL/index.ts. Invoke it after deploying to apply migrations against the live database:

bash
aws lambda invoke \
  --function-name staging-my-api-MigrateDSQL \
  --payload '{}' \
  response.json

Apply permissions

After running mantle generate permissions, apply the generated SQL to Aurora DSQL:

bash
mantle db apply-permissions

Reads SQL from permissions/ and executes against the admin connection. Idempotent — already-applied permissions are skipped.

Other commands

bash
# Push schema directly to the database (development only)
mantle db push

# Open Drizzle Studio web UI
mantle db studio

Next steps

  • Database — Drizzle schema patterns and getDrizzleClient()
  • Entity Queries@RequiresTable and permission generation
  • Infrastructuremantle generate infra for the MigrateDSQL Lambda