The db/ workspace manages the data layer with Drizzle ORM and Neon PostgreSQL. In production, Cloudflare Hyperdrive pools and caches connections at the edge.
db/
├── schema/ # Table definitions and relations
├── migrations/ # Auto-generated SQL migrations
├── seeds/ # Seed data scripts
├── scripts/ # Utilities (seed runner, export)
├── drizzle.config.ts # Drizzle Kit configuration
└── index.ts # Re-exports schema + DatabaseSchema type
Schema files are organized by domain – one file per entity group (e.g., user.ts contains the user, session, identity, and verification tables). All tables are re-exported from schema/index.ts.
The API worker connects to Neon through Cloudflare Hyperdrive, which provides connection pooling and optional query caching at the edge.
Two Hyperdrive bindings are available:
| Binding | Cache | Use for |
|---|---|---|
HYPERDRIVE_CACHED |
60 s query cache | Read-heavy queries where slight staleness is acceptable |
HYPERDRIVE_DIRECT |
None | Writes, real-time reads, anything requiring fresh data |
Both are exposed in tRPC context as ctx.db (cached) and ctx.dbDirect (direct):
// apps/api/lib/db.ts (simplified)
export function createDb(hyperdrive: Hyperdrive) {
const client = postgres(hyperdrive.connectionString, {
max: 1, // single connection per Worker isolate
prepare: false, // required for Hyperdrive compatibility
});
return drizzle(client, { schema, casing: "snake_case" });
}
::: info
In development, Wrangler’s getPlatformProxy() emulates the Hyperdrive bindings locally, resolving them to your DATABASE_URL. Your code uses the same HYPERDRIVE_CACHED / HYPERDRIVE_DIRECT bindings in both environments – no conditional connection logic needed.
:::
Run from the repo root. Append :staging or :prod to target other environments.
| Command | Description |
|---|---|
bun db:generate |
Generate migration SQL from schema changes |
bun db:migrate |
Apply pending migrations |
bun db:push |
Push schema directly (skips migration files) |
bun db:studio |
Open Drizzle Studio browser UI |
bun db:seed |
Run seed scripts |
bun db:check |
Check for drift between schema and migrations |
bun db:export |
Export database via pg_dump to db/backups/ |
bun db:typecheck |
Run TypeScript type-checking on the db/ workspace |
Database scripts select the environment through the ENVIRONMENT variable (falls back to NODE_ENV). Each environment loads env files in priority order:
.env.{env}.local → .env.local → .env
For example, bun db:push:staging loads .env.staging.local first. The DATABASE_URL variable must be a valid postgres:// or postgresql:// connection string.
See Environment Variables for full details.
The @repo/db package exports two entry points:
import * as schema from "@repo/db"; // full schema + DatabaseSchema type
import { user, session } from "@repo/db/schema"; // individual tables