Why Drizzle Over Prisma
January 25, 2025 · Magnus Rødseth
Why Drizzle Over Prisma
The TypeScript ORM landscape has a clear incumbent: Prisma. With its schema-first approach and excellent developer experience, Prisma shaped how a generation of developers think about database access.
But a challenger has emerged. Drizzle ORM takes a fundamentally different approach — and for Eden Stack, that approach won. Here's why.
Two Philosophies of Database Access
Prisma and Drizzle represent two distinct philosophies:
Prisma's philosophy: "Developers shouldn't need to think in SQL. Give them a high-level abstraction, and we'll generate the optimal queries."
Drizzle's philosophy: "If you know SQL, you know Drizzle. Stay close to the metal, with TypeScript safety on top."
Neither philosophy is wrong. They optimize for different priorities.
Prisma: The Abstraction Layer
Prisma has earned its popularity. The developer experience is genuinely excellent:
// Prisma: Schema-first, abstracted queries
// schema.prisma
model User {
id String @id @default(uuid())
email String @unique
posts Post[]
}
model Post {
id String @id @default(uuid())
title String
author User @relation(fields: [authorId], references: [id])
authorId String
}
// Query
const usersWithPosts = await prisma.user.findMany({
include: { posts: true },
});What Prisma does well:
- Exceptional developer experience for common patterns
- Schema-first design with automatic migrations
- Powerful Prisma Studio for visual database exploration
- Large ecosystem and community
- Great documentation
If you're building a straightforward CRUD application and want maximum abstraction from SQL, Prisma delivers.
Where Prisma Struggles
But Prisma's abstraction comes with costs:
1. Bundle Size and Cold Starts
Prisma requires a runtime query engine — a compiled binary that adds significant weight:
| Metric | Prisma | Drizzle |
|---|---|---|
| Bundle size | ~9MB+ | ~7.4KB |
| Cold start impact | Significant | Negligible |
| Dependencies | External binary | Zero runtime deps |
For serverless deployments where cold starts matter, this difference is substantial. Every Lambda invocation, every edge function, every Cloudflare Worker pays this tax.
2. The N+1 Query Problem
Prisma's include syntax is convenient but can hide performance issues:
// This looks innocent
const users = await prisma.user.findMany({
include: { posts: { include: { comments: true } } },
});
// But may generate multiple queries under the hoodPrisma has improved here, but the abstraction can make it harder to reason about what's actually hitting your database.
3. Code Generation Dependency
Prisma requires a generate step. Change your schema, run prisma generate, wait for codegen. It works, but it's friction — and it means your types exist in a generated file, not in your source code.
Drizzle: SQL With Type Safety
Drizzle takes the opposite approach: your schema IS TypeScript, your queries look like SQL, and there's no abstraction hiding what happens.
// Drizzle: TypeScript-first, SQL-aware
import { pgTable, uuid, varchar, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: varchar('email', { length: 255 }).unique().notNull(),
});
export const posts = pgTable('posts', {
id: uuid('id').defaultRandom().primaryKey(),
title: varchar('title', { length: 255 }).notNull(),
authorId: uuid('author_id').references(() => users.id).notNull(),
});
// Query — looks like SQL, fully typed
const usersWithPosts = await db
.select()
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id));Why "If You Know SQL, You Know Drizzle" Matters
Here's the key insight: SQL is a feature, not a bug.
SQL has been refined for 50 years. It's declarative, powerful, and universally understood. When you write a Drizzle query, you can predict exactly what SQL it generates — because the syntax maps directly.
// What you write
const result = await db
.select({
userName: users.name,
postCount: count(posts.id),
})
.from(users)
.leftJoin(posts, eq(posts.authorId, users.id))
.groupBy(users.id)
.having(gt(count(posts.id), 5));
// What runs (predictable)
// SELECT users.name, COUNT(posts.id)
// FROM users
// LEFT JOIN posts ON posts.author_id = users.id
// GROUP BY users.id
// HAVING COUNT(posts.id) > 5For developers who know SQL, this is liberating. No guessing what the ORM will generate. No wondering why a query is slow. The translation is transparent.
Performance Where It Counts
Benchmarks consistently show Drizzle outperforming Prisma, especially in scenarios that matter for modern apps:
| Benchmark | Drizzle | Prisma |
|---|---|---|
| Simple select | ~2-3x faster | Baseline |
| Complex joins | ~2-4x faster | Baseline |
| Cold start | ~10x faster | Baseline |
| Bundle size | 7.4KB | ~9MB |
The cold start advantage is particularly relevant for:
- Serverless functions — Every Lambda, Vercel function, or edge handler benefits
- Edge deployments — Cloudflare Workers, Deno Deploy, etc.
- Microservices — Fast container startup times
Type Safety Without Code Generation
Drizzle's type inference works at compile time, without generating files:
// Your schema defines the types
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: varchar('email', { length: 255 }).notNull(),
role: varchar('role', { length: 50 }).$type<'admin' | 'user'>(),
});
// Types are inferred
type User = typeof users.$inferSelect;
// { id: string; email: string; role: 'admin' | 'user' | null }
type NewUser = typeof users.$inferInsert;
// { id?: string; email: string; role?: 'admin' | 'user' | null }No prisma generate. No watching for schema changes. Your TypeScript just works.
Relational Queries When You Want Them
Drizzle also offers a higher-level Queries API for when you want Prisma-style convenience:
// Drizzle Queries API — more abstract when needed
const usersWithPosts = await db.query.users.findMany({
with: {
posts: {
with: { comments: true },
},
},
});Best of both worlds: SQL-like precision when you need control, relational convenience when you don't.
The Provider-Agnostic Advantage
Here's something often overlooked: Drizzle makes your database choice a configuration detail.
// Switch from Neon to local Postgres to AWS RDS
// by changing one line
import { drizzle } from 'drizzle-orm/neon-http';
// or
import { drizzle } from 'drizzle-orm/postgres-js';
// or
import { drizzle } from 'drizzle-orm/node-postgres';Your schema stays the same. Your queries stay the same. The infrastructure is abstracted at the driver level, not the query level. This matters when:
- You want local Docker development with Neon production
- You might migrate to a different PostgreSQL host
- You need different drivers for different deployment targets
When to Choose What
Choose Prisma when:
- Your team prefers maximum abstraction from SQL
- You're building straightforward CRUD applications
- Prisma Studio is valuable for your workflow
- Bundle size and cold starts aren't concerns
- You like schema-first development with separate files
Choose Drizzle when:
- You're comfortable with SQL and want that control
- Serverless/edge performance matters
- You prefer schema-as-code in TypeScript
- You want minimal bundle size
- Provider flexibility is important
Why Eden Stack Uses Drizzle
For a template designed around TypeScript excellence and deployment flexibility, Drizzle was the clear choice:
- TypeScript-native — Schema defined in TypeScript, types inferred automatically
- Performance — Fast queries, tiny bundle, serverless-optimized
- SQL transparency — Know exactly what's hitting your database
- Provider flexibility — Works with Neon, local Postgres, or any PostgreSQL host
- Modern approach — Built for the serverless era, not adapted to it
Combined with Neon for serverless PostgreSQL, Drizzle provides the database layer Eden Stack needs: type-safe, performant, and infinitely flexible.
The result is a stack where your database access is as transparent as your API types — you always know what's happening, and TypeScript has your back.
This post reflects my opinions after building with both Prisma and Drizzle in production. Prisma is an excellent tool that has served the community well — this isn't about Prisma being bad, but about Drizzle being a better fit for Eden Stack's goals.