- Published on
Drizzle ORM in 2026 — The TypeScript ORM That Replaced Prisma for Performance Teams
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
Prisma became the default ORM for TypeScript developers. It''s beginner-friendly and has excellent documentation. But Prisma has a ceiling.
Prisma''s bundle size is 300KB+. It doesn''t run on Cloudflare Workers or Vercel Edge Functions. Queries are often suboptimal—N+1 problems lurk in eager loading. Performance teams hit the limits and switch.
Drizzle ORM emerged as the alternative. It''s TypeScript-first, produces smaller bundles, runs at the edge, and generates faster queries. By 2026, teams building for performance choose Drizzle.
This post covers why, how to use it, and when to stay with Prisma.
- Why Teams Switch from Prisma to Drizzle
- Drizzle Schema Definition
- drizzle-kit Migrations
- Relations API
- db.$with() for CTEs
- db.transaction()
- Prepared Statements
- Drizzle with PlanetScale/Turso/Neon/Supabase
- drizzle-zod for Schema Validation
- Query Logging and Performance
- Drizzle Studio
- When to Use Drizzle vs Prisma
- Real-World Example: Blog API
- Checklist
- Conclusion
Why Teams Switch from Prisma to Drizzle
Prisma abstracts database differences. It claims to be database-agnostic, but this abstraction costs:
- Bundle size: Prisma generates a large runtime. Edge Functions have cold start penalties and size limits.
- Generated queries: Prisma often generates suboptimal SQL. N+1 queries are easy to accidentally create.
- Edge incompatibility: Prisma relies on filesystem access and native binaries. Cloudflare Workers reject it.
- Performance: For the same query, Drizzle is 2–3x faster (milliseconds matter at scale).
Drizzle sacrifices some convenience for power. You''re closer to the database. That closeness is its strength.
Example query performance:
Prisma: SELECT users WHERE id = ? (plus side query for relations) = 45ms
Drizzle: SELECT users WHERE id = ? (with JOIN in one query) = 15ms
Drizzle is explicit. You decide joins, indexes, and query structure. Prisma tries to be smart and often isn''t.
Drizzle Schema Definition
Drizzle schemas are TypeScript types that double as database schema. They''re human-readable and type-safe.
import { drizzle } from 'drizzle-orm/postgres-js';
import { pgTable, text, serial, timestamp, uuid } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').unique().notNull(),
name: text('name'),
created_at: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
user_id: uuid('user_id').references(() => users.id),
title: text('title').notNull(),
content: text('content'),
created_at: timestamp('created_at').defaultNow(),
});
The schema is your source of truth. Migrations are generated from it.
Initialize Drizzle:
import postgres from 'postgres';
import { drizzle } from 'drizzle-orm/postgres-js';
const client = postgres(process.env.DATABASE_URL);
const db = drizzle(client);
No code generation, no prisma generate step. Types are inferred directly from schema.
drizzle-kit Migrations
drizzle-kit generates SQL migrations from schema changes.
# Generate migration
npx drizzle-kit generate:postgres
# Apply migration
npx drizzle-kit migrate:postgres
# Or programmatically
import { migrate } from 'drizzle-orm/postgres-js/migrator';
await migrate(db, { migrationsFolder: './drizzle' });
Migrations are pure SQL, readable and diffable. You can edit them if needed.
-- drizzle/0001_add_posts_table.sql
CREATE TABLE posts (
id serial PRIMARY KEY,
user_id uuid NOT NULL REFERENCES users(id),
title text NOT NULL,
created_at timestamp DEFAULT now()
);
CREATE INDEX ON posts(user_id);
This is explicit, reviewable, and safe.
Relations API
Drizzle has a "relations" API for defining relationships without foreign key constraints.
import { relations } from 'drizzle-orm';
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.user_id],
references: [users.id],
}),
}));
Now queries can eagerly load relations:
const user = await db.query.users.findFirst({
where: eq(users.id, userId),
with: {
posts: true,
},
});
// user.posts is available, loaded in one query
Drizzle generates a single LEFT JOIN instead of separate queries. Type-safe and efficient.
db.$with() for CTEs
Common Table Expressions (CTEs) are underused in Prisma. Drizzle makes them natural.
const userPostCounts = db.$with('user_post_counts').as(
db.select({
user_id: posts.user_id,
count: sql`COUNT(*)`.as('count'),
}).from(posts).groupBy(posts.user_id)
);
const result = await db
.with(userPostCounts)
.select({
id: users.id,
email: users.email,
post_count: userPostCounts.count,
})
.from(users)
.leftJoin(userPostCounts, eq(users.id, userPostCounts.user_id));
This generates a single query with a CTE. No client-side filtering or multiple queries.
db.transaction()
Transactions ensure atomicity. Drizzle wraps them cleanly.
const result = await db.transaction(async (trx) => {
// Insert user
const newUser = await trx.insert(users).values({
email: 'user@example.com',
name: 'John',
}).returning();
// Insert posts (uses same transaction)
await trx.insert(posts).values({
user_id: newUser[0].id,
title: 'First post',
});
return newUser[0];
});
If either query fails, both rollback. Transaction isolation is automatic.
Prepared Statements
For repeated queries, prepared statements improve performance.
const getUserById = db.query.users.findFirst({
where: eq(users.id, sql.placeholder('id')),
}).prepare();
const user = await getUserById.execute({ id: userId });
The prepared statement is compiled once, executed many times. No query parsing overhead.
Drizzle with PlanetScale/Turso/Neon/Supabase
Drizzle supports multiple databases. The API is consistent; only connection details change.
PostgreSQL (Neon):
import postgres from 'postgres';
import { drizzle } from 'drizzle-orm/postgres-js';
const client = postgres(process.env.NEON_DATABASE_URL);
const db = drizzle(client);
MySQL (PlanetScale):
import mysql from 'mysql2/promise';
import { drizzle } from 'drizzle-orm/mysql2';
const client = await mysql.createConnection(process.env.PLANETSCALE_URL);
const db = drizzle(client);
SQLite (Turso):
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
const client = createClient({
url: process.env.TURSO_URL,
authToken: process.env.TURSO_TOKEN,
});
const db = drizzle(client);
Switch databases by swapping imports. Schema remains the same.
drizzle-zod for Schema Validation
Drizzle integrates with Zod for runtime validation.
import { createInsertSchema } from 'drizzle-zod';
import { z } from 'zod';
const insertUserSchema = createInsertSchema(users);
// Automatically validates email, name, etc.
const result = insertUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
const newUser = await db.insert(users).values(result.data);
One source of truth for database and API validation. No schema duplication.
Query Logging and Performance
Enable query logging to debug performance:
const db = drizzle(client, {
logger: true, // Log all queries
});
Output:
Query: SELECT * FROM users WHERE id = $1
Params: [uuid-value]
Duration: 4ms
For more control, use custom loggers:
const db = drizzle(client, {
logger: new class {
logQuery(query: string, params: unknown[]) {
console.log('Query:', query);
console.log('Params:', params);
}
}(),
});
Monitor slow queries and optimize indexes.
Drizzle Studio
Drizzle Studio is a web-based database browser. View tables, run queries, inspect data—no external tools needed.
npx drizzle-kit studio
Launches on http://localhost:5173. Browse schema, edit rows, write queries.
It''s lightweight and useful for debugging.
When to Use Drizzle vs Prisma
Use Drizzle if:
- Performance is critical (<50ms query latency)
- You deploy to edge functions (Cloudflare, Vercel Edge)
- You want bundle size <50KB
- You prefer explicit SQL
- You need prepared statements
- You use multiple databases
Use Prisma if:
- You''re a solo founder prioritizing speed-to-market
- You don''t care about bundle size
- You want automatic N+1 detection (Prisma does this better)
- Your queries are simple (CRUD)
- You want the largest community/ecosystem
For most new projects in 2026, Drizzle is the better choice. Prisma optimizes for ease; Drizzle optimizes for power.
Real-World Example: Blog API
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { eq, desc } from 'drizzle-orm';
import { users, posts } from './schema';
const client = postgres(process.env.DATABASE_URL);
const db = drizzle(client);
// Get user with recent posts
export async function getUserWithPosts(userId: string) {
return db.query.users.findFirst({
where: eq(users.id, userId),
with: {
posts: {
orderBy: [desc(posts.created_at)],
limit: 10,
},
},
});
}
// Get post with author
export async function getPost(postId: number) {
return db.query.posts.findFirst({
where: eq(posts.id, postId),
with: {
author: true,
},
});
}
// Create post
export async function createPost(userId: string, title: string, content: string) {
return db.insert(posts).values({
user_id: userId,
title,
content,
}).returning();
}
Clean, type-safe, and generates optimal SQL. No surprises.
Checklist
- Install
drizzle-ormand driver for your database - Define schema in TypeScript
- Generate initial migration
- Create database and apply migration
- Write basic queries (select, insert, update)
- Add relations and test eager loading
- Test transaction behavior
- Set up query logging
- Benchmark against Prisma (query latency)
- Configure drizzle-kit for your workflow
Conclusion
Drizzle ORM represents the next generation of TypeScript database tools. It''s faster, lighter, and more explicit than Prisma. For performance teams and edge-first applications, it''s the clear choice.
The learning curve is slightly steeper, but the payoff is worth it: sub-millisecond queries, edge compatibility, and bundle sizes that don''t bloat your application.
If you''re starting a new project in 2026, evaluate Drizzle. You''ll likely choose it.