ADR-006: Drizzle ORM over Prisma
Date: 2024-01-25 Status: Accepted
Context
LumiBase needs an ORM for PostgreSQL with these requirements:
- Works in Cloudflare Workers — Prisma historically required a query engine binary and Node.js APIs; its edge-compatible
@prisma/adapter-pg/neonadapter was in early preview at design time. - Supports raw SQL / JSONB queries — LumiBase's data model stores collection field configs and permission rules as JSONB. Complex queries need
jsonb_array_elements, custom operators (@>,?), and raw expression support. - Fully type-safe — schema changes should produce TypeScript compile errors where query shapes are wrong.
- Schema-first and migration-friendly — migrations need to be generated from schema changes and run in a controlled manner (not auto-applied on startup).
Frameworks evaluated:
- Prisma — mature, great DX, but edge runtime support was unstable; Rust query engine not compatible with Workers
- Kysely — query builder, not a full ORM; no migrations
- Drizzle — schema-first, generates SQL migrations, no query engine binary, works in Workers via
postgres.jsHTTP mode - MikroORM — Node.js-specific, not Workers-compatible
Decision
Use Drizzle ORM (drizzle-orm + drizzle-kit) with the postgres.js driver.
Schema lives in packages/database/src/schema/, organized into files:
core.ts— sites, settingsaccess.ts— roles, policies, permissions, users, teamscms.ts— collections, fields, relations, items, revisions, activityplatform.ts— files, webhooks, extensions, presets, flows, operationsai.ts— ai_approvals, ai_conversations, ai_messagessearch.ts— search indexes
Migration SQL is generated via drizzle-kit generate and applied via drizzle-kit migrate. Migrations are committed to packages/database/src/migrations/.
Consequences
Positive:
- Zero external dependencies at runtime — no binary query engine, no JIT compilation
- Works in Workers via
postgres.jsin HTTP/WebSocket mode (compatible with Hyperdrive) - Full TypeScript inference from schema → query results
- Fine-grained SQL control when needed (
sql<string>escape hatch) - Migration files are plain SQL — reviewable, reversible, and deployable via CI
Negative:
- Less mature ecosystem than Prisma — fewer community resources and third-party plugins
- No built-in soft-delete or audit hooks (implemented manually in
RevisionServiceandActivityService) - Schema definition syntax is more verbose than Prisma's (
schema.prismaDSL is more concise) - Drizzle relations API (for nested queries) has a learning curve and occasional edge cases with complex JSONB queries
Neutral:
packages/databaseexports both the Drizzle schema and a typeddbinstance; apps import from@lumibase/database