Database layer using Drizzle ORM and PostgreSQL (Neon) via Cloudflare Hyperdrive.
Documentation | Schema | Migrations
db/
├── schema/ # Table definitions and relations
├── migrations/ # Auto-generated migration files
├── seeds/ # Seed scripts (e.g., users)
├── scripts/ # DB utilities (seed/export)
├── drizzle.config.ts # Drizzle configuration
└── package.json # DB-only scripts and depsDATABASE_URLis required and loaded from repo root:.env.<environment>.local→.env.local→.env.- Environment selection:
ENVIRONMENTtakes priority, otherwiseNODE_ENV=production|staging|testfalls back toprod|staging|test; default isdev.
Example .env.dev.local (at repo root):
DATABASE_URL=postgresql://user:password@host:5432/databaseFrom the repo root:
bun db:push # Apply schema (drizzle-kit push)
bun db:generate # Generate migration from schema changes
bun db:migrate # Run pending migrations
bun db:studio # Open Drizzle Studio
bun db:seed # Run seed scripts
bun db:check # Drift checkAppend :staging or :prod to target other environments:
bun db:push:staging # Uses .env.staging.local → .env.local → .env
bun db:push:prod # Uses .env.prod.local → .env.local → .env
bun db:seed:prod
bun db:studio:prod- Update schema in
db/schema. - Generate a migration:
bun db:generate --name <migration-name>. - Apply locally:
bun db:migrate(ordb:pushfor schema sync). - Validate in Drizzle Studio:
bun db:studio. - Apply to staging/prod with the matching
:stagingor:prodsuffix.
import { schema } from "@repo/db";
import { user } from "@repo/db/schema/user";
import { organization, member } from "@repo/db/schema/organization";All primary keys use application-generated prefixed CUID2 IDs (e.g. usr_ght4k2jxm7pqbv01). IDs are generated at the application level via $defaultFn() -- no database-level defaults. See db/schema/id.ts for the prefix map.