Database and migrations

Drizzle push for local dev, migrate for UAT/prod, generate, and the better-auth schema output path.

Package: @taman/db-pg (packages/server/db-pg). Config: drizzle.config.ts.

export default defineConfig({
  dialect: 'postgresql',
  schema: './src/schemas/index.ts',
  out: './migrations',
  dbCredentials: { url: DATABASE_URL },
});

DATABASE_URL is required (nx injects it from .env / .env.uat / .env.prod).

Local development

docker compose up -d
pnpm db-pg:migrate:dev   # drizzle-kit push

Push applies the TypeScript schema directly. There is no packages/server/db-pg/migrations directory in the repository yet. Do not assume SQL files exist until you run generate.

UAT / production

pnpm db-pg:migrate:uat    # drizzle-kit migrate + .env.uat
pnpm db-pg:migrate:prod

These need a migrations folder from pnpm db:generate.

Generate SQL

pnpm db:generate          # drizzle-kit generate → ./migrations

Commit the SQL. Then migrate in environments that must not push.

better-auth schema

pnpm db:generate:auth

nx runs:

pnpx @better-auth/cli generate --config ./gen-auth-schema.ts --output ./src/schema/auth.schema.ts -y
The CLI writes src/schema/auth.schema.ts (singular). The package actually exports src/schemas/auth.schema.ts (plural), which is what drizzle.config.ts and src/index.ts import. After generating, move or merge the file into src/schemas/ or the new file will be unused.

Auth tables (user, session, account, verification, organization, team, members, invitation) already live in src/schemas/auth.schema.ts. Re-generate when you change better-auth plugins or additional user fields.

Studio

pnpm dev:db-pg:studio     # configurations: dev | uat | prod

Client

getDrizzleClient(databaseUrl) in @taman/db-pg. The API uses NITRO_DATABASE_URL. Seed scripts accept NITRO_DATABASE_URL or DATABASE_URL.

IDs: uuidv7() SQL default on todos; better-auth uses JS uuidv7() in generateId.

Pattern

New tables go in src/schemas/<feature>.schema.ts, re-exported from src/schemas/index.ts. Org-scoped resources should references(() => organizationTable.id, { onDelete: 'cascade' }).