Drizzle ORM
risingA lightweight, type-safe TypeScript ORM that feels like writing SQL. Zero runtime overhead, excellent migration tooling, and first-class serverless support.
Quick Verdict
Drizzle is the ORM that TypeScript developers who know SQL actually enjoy using. It maps 1:1 to SQL — if you can write a JOIN, you know Drizzle. Zero runtime overhead, excellent type inference, and the best serverless database story in the ecosystem.
When to use it: You're on serverless, you know SQL, and you want AI tools to generate accurate database queries.
When not to: Your team prefers high-level abstractions and doesn't want to think in SQL.
Best For
- Serverless deployments — no binary, no engine to boot, ~50ms cold start vs Prisma's 300–500ms
- AI-assisted development — the SQL-like API maps to what LLMs know, fewer hallucinations
- Teams that know SQL — Drizzle doesn't abstract it away, it types it
- Next.js + Neon — the native serverless driver was built for this combination
Avoid If
- Your team prefers a higher-level ORM abstraction (Prisma's
findMany+includeis more ergonomic for non-SQL thinkers) - You need the Prisma ecosystem (Accelerate, Pulse, Studio, many third-party adapters)
- Non-TypeScript project — Drizzle doesn't exist outside TypeScript
Why People Choose It
Prisma dominated the TypeScript ORM space, but developers grew frustrated with the Rust-based query engine, the generated client, and cold-start overhead in serverless. Drizzle emerged as the "type safety on top of SQL I already understand" alternative.
The serverless story sealed the deal. Drizzle's zero-overhead design means no binary dependencies and native support for HTTP-based database drivers (Neon serverless, PlanetScale serverless). In a Vercel function, that's 50ms vs 500ms cold starts.
Hidden Costs
Essentially zero direct cost — Drizzle and drizzle-kit are free. The real cost is migration tooling maturity. drizzle-kit is younger than prisma migrate and has had edge cases with complex schema changes. Always test migrations in a branch before applying to production.
Correct vs Cargo-Culted Patterns
Wrong — using the relational API for complex queries:
// ❌ The relational API is convenient but inflexible
const result = await db.query.users.findMany({
with: { posts: { where: { published: true } } },
})Right — SQL-like builder for complex cases:
// ✅ Explicit, readable, performant
const result = await db
.select({ user: users, post: posts })
.from(users)
.leftJoin(posts, and(eq(posts.userId, users.id), eq(posts.published, true)))
.where(eq(users.active, true))Wrong — forgetting atomicity:
// ❌ Two separate awaits — no transaction
await db.insert(orders).values({ ... })
await db.update(inventory).set({ ... })Right — transaction wrapper:
// ✅ Atomic — both succeed or both fail
await db.transaction(async (tx) => {
await tx.insert(orders).values({ ... })
await tx.update(inventory).set({ ... })
})AI Coding Notes
Drizzle produces the most accurate AI-generated database code of any TypeScript ORM. The SQL-like API closely matches what LLMs know about SQL. Always provide your schema.ts file as context — this dramatically improves query generation accuracy.
Common AI Mistakes
- Wrong driver import —
drizzle-orm/postgres-jsvsdrizzle-orm/neon-serverless— different packages, different connection patterns. Match to your database provider. - Missing transactions — AI generates multi-table writes as sequential awaits, not wrapped in
db.transaction(). Atomicity bugs surface under concurrent load. - Relational API for complex queries — elegant for simple cases, inflexible for complex joins. Use the SQL-like builder.
- Not running drizzle-kit — Drizzle does not auto-migrate. Always run
drizzle-kit generate+drizzle-kit pushafter schema changes. - Ignoring
$inferInsert/$inferSelect— these give you insert/select types from the schema. Use them instead of manually defining interfaces.
Start With / Grow Into / Avoid Until Needed
Start with Drizzle if you're on serverless and your team knows SQL. The learning curve is minimal for SQL-fluent developers.
Grow into the relational API (db.query.*) for simple CRUD once you're comfortable — it's more ergonomic for basic operations.
Avoid until needed: Raw SQL escapes (db.execute(sql\...`)`). Use only when the query builder genuinely can't express what you need.
Migration Implications
Moving from Drizzle to Prisma (or vice versa) is 2–5 days of query rewrites for a mid-size app:
- Convert schema definitions
- Rewrite all queries to the new API
- Re-establish migration history
The database itself (Postgres) is portable — it's the query layer that requires work.
Alternatives
- Prisma — richer abstraction, larger ecosystem, better GUI tooling. Heavier runtime, slower in serverless. See Drizzle vs Prisma.