ORM for the Postgres layer. See the drizzle skill for query/schema syntax.
Why was it chosen?
- Always emits exactly 1 SQL query per call — matters on Neon's serverless driver routed over HTTP fetch (
poolQueryViaFetch). - Zero-dependency, tree-shakeable — fits Vercel serverless cold starts.
- Native Neon and Better Auth adapters.
Connection
Config lives in drizzle.config.ts — points to schema directory, sets dialect and output.
Bun + drizzle-kit WebSocket gotcha:
drizzle-kit auto-detects @neondatabase/serverless and internally switches to WebSocket mode. Bun's ws implementation mishandles the upgrade (HTTP 101). Fix: set neonConfig.poolQueryViaFetch = true before defineConfig to force HTTP fetch instead. See drizzle-orm#4957.
Schema management
Two tools for two stages — one TypeScript schema as the single source of truth.
db:push(bunx drizzle-kit push) — diffs schema against live DB and applies directly. Best for development. Use--strictfor approval before SQL executes.generate+migrate— produces versioned SQL files for ordered, reviewable changes. Required for production databases with real data. See drizzle-workflow.md for the full dev→prod workflow.db:studio(bunx drizzle-kit studio) — visual database browser.
Custom schemas (pgSchema)
When using custom PostgreSQL schemas:
- Every
pgSchema()object must be exported — otherwise silently omitsCREATE SCHEMA IF NOT EXISTS - Every
pgEnum()object must be exported — otherwise silently omitsCREATE TYPE drizzle.config.tsmust list all schemas inschemaFilter(only affectspush/pull, notgenerate)
Better Auth adapter: model→table by export-const name
The drizzleAdapter resolves a Better Auth model to its Drizzle table by the exported const name — db is built by spreading the schema, so the export key is the lookup key, not the SQL table name.
export const passkey/export const twoFactor(camelCase) are mandatory export names.- SQL table names are free to be snake_case (
auth.passkey,auth.two_factor). - Field keys must match the model exactly — e.g.
credentialID(notcredential_id) as the property key.
Get the export name wrong and the adapter writes to the wrong table or silently fails to find one. See ../auth/better-auth.md and ../../blueprint/auth.md.
Known limitations
- Only query results are typed — invalid queries still compile.
- Type-checking slows as the schema grows (5,000+ type instantiations).
- Relational Queries API has no mutations.
Related
- postgres.md - Database
- ../auth/better-auth.md - Auth integration
- ../../blueprint/data/drizzle-workflow.md - Dev→prod migration workflow