There’s been a quiet shift in how TypeScript developers are handling database access. Prisma had a long run as the default answer — it’s polished, well-documented, and the schema-first approach made sense to a lot of teams. But in the past two years, Drizzle ORM has been eating into that mindshare, and the reasons are worth understanding because they’re not just preference — they reflect genuine technical tradeoffs.
What Makes Drizzle Different
Drizzle sits closer to SQL than most ORMs. It doesn’t try to abstract away the database or pretend SQL doesn’t exist. Instead, it gives you a TypeScript-native query builder that maps almost directly to SQL, with full type inference throughout.
Here’s a basic example. You define your schema in TypeScript:
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: integer('author_id').references(() => users.id),
});
Then your queries look like this:
import { db } from './db';
import { users, posts } from './schema';
import { eq, desc } from 'drizzle-orm';
// Select with join — fully typed
const result = await db
.select({
postTitle: posts.title,
authorName: users.name,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(users.id, 1))
.orderBy(desc(posts.id))
.limit(10);
// result is: Array<{ postTitle: string; authorName: string | null }>
That return type is inferred automatically. Not generated by a codegen step. Not a loose any. TypeScript knows exactly what the result of this query is based on the schema definition and the select clause you wrote. If you add a non-nullable column and reference it in a join context where null is possible, TypeScript catches it.
Why People Are Moving From Prisma
To be fair to Prisma, it’s a solid tool. But there are a few consistent pain points that drive developers toward Drizzle.
Codegen and schema sync: Prisma uses a separate schema language (.prisma files) and generates TypeScript types from them. This means a codegen step in your workflow and a risk of your schema and generated types falling out of sync. Drizzle’s schema is TypeScript — there’s nothing to generate.
Cold start performance: Prisma’s query engine is a separate binary that has to start up. In serverless environments (Vercel, Cloudflare Workers, AWS Lambda), this adds meaningful cold start latency. Drizzle is pure JavaScript/TypeScript with no binary — it starts essentially instantly, which is why it’s become the default choice in the Cloudflare Workers ecosystem.
SQL proximity: Prisma abstracts heavily. If you write complex queries, you often end up writing raw SQL anyway because Prisma’s API can’t express what you need. Drizzle’s mental model matches SQL more closely, so the API covers more ground before you need to drop to raw queries.
The tradeoff: Prisma has a more polished migration workflow and a slightly gentler learning curve for developers who haven’t spent much time writing SQL. Drizzle assumes you’re comfortable thinking in SQL terms. If you are, the payoff is significant. If you’re not, there’s a steeper ramp.
Database Support
Drizzle supports PostgreSQL, MySQL, SQLite, and several edge-native variants: Cloudflare D1, Turso (libSQL), Bun’s SQLite driver, PlanetScale, Neon, and others. The schema definitions and query syntax are consistent across drivers — switching from local SQLite in development to PostgreSQL in production means changing your database connection setup, not your queries.
For SQLite specifically, Drizzle has become the de facto standard in the edge computing space. Cloudflare D1 + Drizzle + Hono is a common stack for lightweight APIs that need to run globally at the edge with low latency.
Migrations
Drizzle Kit handles migrations. You run drizzle-kit generate and it diffs your current schema against the previous migration state, producing a SQL migration file. You review that file (it’s plain SQL, not Drizzle-specific syntax), then apply it with drizzle-kit migrate.
npx drizzle-kit generate
# Generates: drizzle/0001_add_posts_table.sql
npx drizzle-kit migrate
# Applies pending migrations
The generated SQL is readable and reviewable. Some developers appreciate this more than migration tools that produce opaque operations — you can see exactly what’s hitting your database before it runs.
For existing databases, drizzle-kit introspect generates a Drizzle schema from your existing database structure, which makes it easier to adopt Drizzle incrementally in an existing project rather than starting from scratch.
A Realistic Setup for a Next.js Project
// lib/db.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
// schema.ts
import { pgTable, serial, varchar, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 100 }),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
// app/api/users/route.ts (Next.js App Router)
import { db } from '@/lib/db';
import { users } from '@/lib/schema';
export async function GET() {
const allUsers = await db.select().from(users);
return Response.json(allUsers);
}
export async function POST(request: Request) {
const { email, name } = await request.json();
const [newUser] = await db.insert(users).values({ email, name }).returning();
return Response.json(newUser, { status: 201 });
}
The returning() method is PostgreSQL-specific and gives you back the inserted row with database-generated values (like the ID and timestamp) fully typed.
Drizzle Studio
Drizzle includes a browser-based data viewer called Drizzle Studio, accessible via npx drizzle-kit studio. It connects to your database using your Drizzle schema and gives you a table browser, query interface, and basic data editing. It’s useful for local development and debugging without needing to reach for a separate database client.
Is It the Right Choice for Your Project?
Drizzle makes sense if you’re comfortable with SQL, working in a TypeScript-first stack, and want to avoid codegen friction. It’s particularly compelling for serverless or edge deployments where cold start matters, and for teams that want their database queries to be as predictable and readable as possible.
Prisma is still a fine choice if you want a more opinionated setup, if you value the migration GUI experience, or if your team includes developers who aren’t SQL-fluent.
Kysely is another alternative worth knowing — similar philosophy to Drizzle (SQL-close, TypeScript-native) but with a slightly different API shape. Some developers prefer Kysely’s query builder ergonomics. Worth looking at if Drizzle’s approach clicks but you want to compare.
For new TypeScript projects starting in 2026, Drizzle is the more commonly recommended starting point. The ecosystem support, the zero-codegen workflow, and the edge performance characteristics have won over a lot of teams who went through the Prisma path first.