DB module
Drizzle ORM and Cloudflare D1 schema, migrations, and access patterns.
DB module
The database uses Drizzle ORM with Cloudflare D1 (SQLite). D1 is configured via bindings in wrangler.jsonc, not environment variables (see Env for project env overview). Table definitions live in two files: auth.schema.ts (Better Auth, script-generatable) and app.schema.ts (application tables). Both are merged in schema.ts into a single schema for Drizzle and migrations.
Directory structure
src/db/
├── index.ts # getDb() using env.DB (cloudflare:workers), re-exports schema
├── schema.ts # Merges auth.schema + app.schema, exports schema and re-exports tables
├── auth.schema.ts # Source-aligned auth tables: user, session, account, verification + relations
├── app.schema.ts # Source business tables
├── types.ts # User, ApiKey, business row types ($inferSelect from tables)
└── migrations/ # Drizzle-generated SQL and metaConfiguration
- wrangler.jsonc
d1_databases:binding: "DB",database_id,database_name,migrations_dir: "./src/db/migrations".
- At runtime the D1 instance is available as
env.DB(Cloudflare Workers). The project usesimport { env } from "cloudflare:workers"ingetDb(), so no argument is passed.
Core API
-
getDb()
- Returns
drizzle(env.DB, { schema })for all server-side DB access. Theschemais built inschema.tsfromauth.schemaandapp.schema. Called without arguments (usesenvfromcloudflare:workers).
- Returns
-
Exports (
index.ts)- Re-exports from
schema.ts(which re-exportsauth.schemaandapp.schema). Import tables from@/dbor@/db/auth.schema/@/db/app.schema.
- Re-exports from
-
types.ts
User,ApiKey,UserFiles— inferred from table$inferSelect. Use for API responses and admin tables.
Schema design
-
auth.schema.ts
- Auth tables aligned to
/Users/rusk/developer/project-template/src/db/schema.ts:user,session,account,verification, plus Drizzle relations. Do not add target-only compatibility fields such asnormalizedEmailorcustomerId.
- Auth tables aligned to
-
app.schema.ts
- Source business tables such as
api_key,order,subscription,credit, RBAC, content, and keyword tables. Target-only compatibility tables are not kept.
- Source business tables such as
-
schema.ts
- Re-exports all tables from
auth.schemaandapp.schema; exportsschema = { ...authSchema, ...appSchema }. Used bygetDb()and drizzle-kit as the single schema entry point.
- Re-exports all tables from
Migrations
- Generate:
pnpm db:generate(usesdrizzle.config.tsor local config). - Apply:
- Local D1:
pnpm db:migrate:local. - Remote D1:
pnpm db:migrate:remote.
- Local D1:
- Push schema (dev only):
pnpm db:push. - drizzle-kit:
- Remote:
drizzle.config.ts(D1 HTTP + account credentials). - Local:
drizzle.config.local.ts(local SQLite path, e.g. for Studio).
- Remote:
Consumers
- Auth (
src/auth/auth.ts):drizzleAdapter(getDb(), { provider: 'sqlite' }); Better Auth reads/writesuser,session,account, andverification. User-facing API keys use the sourceapi_keybusiness table, not Better Auth'sapikeytable. - Other server logic:
import { getDb } from '@/db'and callgetDb(), then run queries. Import tables from@/dbor@/db/auth.schema/@/db/app.schemaas needed.
Notes
- D1 is SQLite; use Drizzle’s SQLite dialect (
sqliteTable,text,integer, etc.). - All DB access must run in the Worker or server;
getDbdepends onD1Databaseand is not for use in the browser.