DEV Community

Cover image for Drizzle vs Prisma in 2026: Benchmarks, Drizzle 1.0, Prisma 8 and Which to Choose
WaseemAnjum
WaseemAnjum

Posted on Originally published at fabwaseem.com

Drizzle vs Prisma in 2026: Benchmarks, Drizzle 1.0, Prisma 8 and Which to Choose

I benchmarked Drizzle 0.45, the Drizzle 1.0 RC, Prisma 7 and the Prisma 8 RC against the same PostgreSQL 17 database. The original, with any updates, lives on fabwaseem.com.

Drizzle vs Prisma comes down to how you want to write data access in TypeScript. Drizzle is a thin, SQL-shaped query builder: your schema is TypeScript and every query maps closely to the SQL it sends. Prisma is a higher-level ORM: you write a schema file and get a generated, model-based client. Both are type-safe, and both work well with PostgreSQL and Next.js.

In October 2026 both are in the middle of a major version. Drizzle 1.0 is a release candidate while 0.45.4 is still npm latest. Prisma 8 is a release candidate that npm latest already installs. So I benchmarked all five combinations against the same PostgreSQL 17 database. On simple queries the gap was a tenth of a millisecond. On one common query pattern it was 6-19x, and the cause was the shape of the SQL, not the ORM.

TL;DR: Drizzle or Prisma in 2026?

Pick Drizzle if you think in SQL, deploy to serverless or edge, and want the smallest install and fastest cold start: 30 MB and about 35 ms bundled in my test. Pick Prisma 7 if you want the most guided workflow, a generated client and Prisma Migrate. Wait for GA before moving production to Drizzle 1.0 or Prisma 8.

Drizzle Prisma
Stable line 0.45.4 (npm latest) 7.10.1 (npm prev)
Next major 1.0.0-rc.4 (npm rc) 8.0.0-rc (npm latest)
Schema lives in TypeScript (pgTable) schema.prisma (7) or contract.prisma (8)
Code generation None prisma generate (7), prisma contract emit (8)
Query style SQL builder + relational db.query Model client (7), chained db.orm (8)
Migrations drizzle-kit generate / migrate / push Prisma Migrate
Production install (my test) 21-30 MB, 18 packages 329 MB (7), 130 MB (8)
Bundled cold start (my test) ~35 ms ~100 ms (7), ~370 ms (8 RC)

What is the difference between Drizzle and Prisma?

Drizzle defines tables in TypeScript and gives you a typed SQL builder, so db.select().from(users).where(...) reads like the SQL it sends, with no generate step. Prisma defines models in its own schema language, generates a client and hides SQL behind model methods. Drizzle is closer to the database; Prisma is closer to your domain model.

The same table in each:

```ts title="schema.ts (Drizzle 1.0)"
import { defineRelations } from "drizzle-orm";
import { boolean, index, integer, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("User", {
id: serial().primaryKey(),
email: text().notNull().unique(),
name: text(),
createdAt: timestamp({ precision: 3, mode: "date" }).notNull().defaultNow(),
});

export const posts = pgTable("Post", {
id: serial().primaryKey(),
title: text().notNull(),
published: boolean().notNull().default(false),
views: integer().notNull().default(0),
authorId: integer().notNull().references(() => users.id, { onDelete: "cascade" }),
}, (t) => [index("Post_authorId_idx").on(t.authorId)]);

export const relations = defineRelations({ users, posts }, (r) => ({
users: { posts: r.many.posts() },
posts: { author: r.one.users({ from: r.posts.authorId, to: r.users.id }) },
}));






```prisma title="prisma/schema.prisma (Prisma 7)"
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  posts     Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  published Boolean @default(false)
  views     Int     @default(0)
  authorId  Int
  author    User    @relation(fields: [authorId], references: [id], onDelete: Cascade)

  @@index([authorId])
}
Enter fullscreen mode Exit fullscreen mode

How do queries compare in Drizzle and Prisma?

All four clients can fetch a user with their posts in one call. Drizzle 1.0 uses object filters in db.query, Prisma 7 uses findMany with include, and Prisma 8 chains .include("posts") on a collection. The type-safety level is the same; the difference is whether the code looks like SQL or like method calls.

These are the exact queries from my benchmark, and all of them type-check under strict:

```ts title="Fetch 20 users with their posts"
// Drizzle 0.45 (RQB v1)
db.query.users.findMany({ with: { posts: true }, orderBy: (u, { asc }) => [asc(u.id)], offset, limit: 20 });

// Drizzle 1.0 (RQB v2)
db.query.users.findMany({ with: { posts: true }, orderBy: { id: "asc" }, offset, limit: 20 });

// Prisma 7
prisma.user.findMany({ include: { posts: true }, orderBy: { id: "asc" }, skip: offset, take: 20 });

// Prisma 8 RC
db.orm.public.User.include("posts").orderBy((u) => u.id.asc()).offset(offset).limit(20).all();




Drizzle also keeps its plain SQL builder (`db.select().from(posts).innerJoin(...)`) for anything the relational API does not cover. Prisma 7 falls back to `$queryRaw` or TypedSQL, and Prisma 8 has its own SQL builder. I covered Prisma 8's query API in detail in my [Prisma 8 guide](https://www.fabwaseem.com/blog/prisma-8).

## Is Drizzle faster than Prisma?

For single-row reads and inserts, barely. In my test every client answered a primary-key lookup in 0.12-0.20 ms against local PostgreSQL, and over a network that gap disappears. The real difference was in nested pagination: with `OFFSET`, single-query JSON loading got 6-11x slower than Prisma 7's two-query default.

### Benchmark setup

- PostgreSQL 17.11 on the same machine (8 vCPU Xeon, 15 GB RAM), Node.js 24.22.0, 1,000 users and 10,000 posts with an index on `Post.authorId`.
- Each case: 200 warm-up calls, then 2,000 timed calls. Each client ran 3 times, with inserted rows deleted before each run. The table shows the median of the three per-run medians.
- Versions: Prisma 7.10.1 with `@prisma/adapter-pg`, Drizzle 0.45.4, Drizzle 1.0.0-rc.4 (with and without `jit: true`), and Prisma 8 (`@prisma/orm-postgres` 8.0.0-rc.18).
- "Deep page" means 20 users plus their posts at `OFFSET` 0 to 960.

### Results (median, milliseconds)

| Query | Prisma 7.10.1 | Drizzle 0.45.4 | Drizzle 1.0 rc.4 | Drizzle 1.0 `jit` | Prisma 8 rc.18 |
| --- | --- | --- | --- | --- | --- |
| Find user by id | 0.150 | 0.129 | **0.121** | 0.136 | 0.195 |
| First page, 20 users + posts | 0.730 | 0.719 | **0.561** | 0.617 | 1.185 |
| Deep page, 20 users + posts | **0.749** | 4.650 | 5.164 | 5.287 | 8.060 |
| Insert one post | 0.168 | 0.146 | **0.139** | 0.164 | 0.208 |
| Process RSS after run | 289 MB | 251 MB | 295 MB | 296 MB | 404 MB |

Two things stand out. Drizzle 1.0 was the fastest at nearly everything, but `jit: true` made no measurable difference here. Drizzle documents it as "just-in-time compiled row mappers," and mapping 20 rows is not where the time goes. And the deep-page row looks like a Prisma win, but it isn't one, as the next section shows.

## Why are nested queries with OFFSET slow?

Drizzle and Prisma 8 load relations in one SQL statement, using a JSON subquery for each parent row. PostgreSQL applies `OFFSET` last, so it builds the JSON for every skipped row and then throws it away. At `OFFSET 960`, `EXPLAIN ANALYZE` showed the posts subquery running 980 times to return 20 users.

![Why OFFSET is slow with single-query relation loading: PostgreSQL builds JSON posts for every skipped row before applying OFFSET, while two-query loading and keyset pagination only touch 20 users](https://res.cloudinary.com/wasicloudinary/image/upload/v1792248750/fabwaseem/blog/drizzle-vs-prisma/diagram-sql.jpg)

This is the SQL Drizzle 0.45 generated (Drizzle 1.0 is nearly identical):



```sql
select "users"."id", "users"."email", "users"."name", "users"."createdAt", "users_posts"."data" as "posts"
from "User" "users"
left join lateral (
  select coalesce(json_agg(json_build_array(...)), '[]'::json) as "data"
  from "Post" "users_posts" where "users_posts"."authorId" = "users"."id"
) "users_posts" on true
order by "users"."id" asc limit $1 offset $2
Enter fullscreen mode Exit fullscreen mode

The PostgreSQL docs warn about this directly: "The rows skipped by an OFFSET clause still have to be computed inside the server; therefore a large OFFSET might be inefficient" (PostgreSQL 17, LIMIT and OFFSET). Here, each skipped row includes a full posts subquery.

Prisma 7 avoided it only because it sent two queries: the 20 users, then SELECT ... FROM "Post" WHERE "authorId" IN (...). When I turned on Prisma 7's relationJoins preview and set relationLoadStrategy: "join", the deep page went from 0.79 ms to 14.8 ms, the slowest result in the whole test. Prisma's relation queries docs say join uses "a combination of LATERAL JOINs and JSON aggregation", which is the same shape.

How do I fix slow nested pagination?

Use keyset (cursor) pagination instead of OFFSET: ask for "the 20 users after id X". PostgreSQL then seeks straight to the right index position and builds JSON for only 20 rows. In my test, Drizzle 1.0 deep pages dropped from 5.16 ms to 0.66 ms and Prisma 8 from 8.06 ms to 1.29 ms.

```ts title="Keyset pagination (both tested)"
// Drizzle 1.0
db.query.users.findMany({ with: { posts: true }, where: { id: { gt: afterId } }, orderBy: { id: "asc" }, limit: 20 });

// Prisma 8 RC
db.orm.public.User.where((u) => u.id.gt(afterId)).include("posts").orderBy((u) => u.id.asc()).limit(20).all();

// Prisma 7
prisma.user.findMany({ include: { posts: true }, cursor: { id: afterId }, skip: 1, take: 20, orderBy: { id: "asc" } });




Prisma's [pagination guide](https://www.prisma.io/docs/orm/prisma-client/queries/pagination) recommends the same: use cursor-based pagination "when you care more about performance and consistency as the dataset grows." If you need page numbers, run the parent query first and load children with a second `IN (...)` query. That is exactly what Prisma 7's default does.

## Which has a faster cold start, Drizzle or Prisma?

Drizzle, once bundled. With esbuild, importing the client and running the first query took about 35 ms with Drizzle, about 100 ms with Prisma 7 and about 370 ms with Prisma 8 RC. Unbundled, Drizzle was slower than Prisma 7, because Node loaded 425-528 separate Drizzle module files from `node_modules`.

| Median of 5 process starts | Prisma 7.10.1 | Drizzle 0.45.4 | Drizzle 1.0 rc.4 | Prisma 8 rc.18 |
| --- | --- | --- | --- | --- |
| Unbundled: import + first query | 55 + 89 ms | 400 + 31 ms | 699 + 18 ms | 494 + 30 ms |
| esbuild bundle: import + first query | 21 + 78 ms | 17 + 19 ms | 16 + 18 ms | 342 + 26 ms |
| Bundle size | 5.0 MB | 448 KB | 476 KB | 1.8 MB |

Next.js and most serverless builds bundle server code, so the bundled row is the one that usually applies. Prisma 7's import is fast, but its first query costs about 78 ms; the client ships its query compiler as base64-encoded WebAssembly, which is the likely cost of that first call. Prisma 8 RC's time goes into startup itself. A CPU profile showed the top functions inside arktype's `@ark/schema` and `@ark/util`, which Prisma 8 uses for runtime validation. That is RC behaviour and may change before GA.

## How big is Drizzle vs Prisma in node_modules?

Drizzle installed in 21 MB (0.45.4) or 30 MB (1.0 RC) with 18 packages, including `pg`. With `npm ci --omit=dev`, Prisma 7 came to 329 MB and 345 packages, and Prisma 8 RC to 130 MB and 118 packages. For Docker images and serverless size limits, that is the clearest difference between them.

Where the Prisma weight comes from, according to `npm ls` and `du`:

- **Prisma 7:** `@prisma/client` (72 MB) lists `prisma` and `typescript` as optional peer dependencies. My project had both as devDependencies, so npm kept them in the production tree, along with the CLI, `@prisma/studio-core` (43 MB) and `@prisma/dev`.
- **Prisma 8 RC:** `@prisma/orm-postgres` depends on `@prisma/orm-toolchain`, which brings `typescript`, `prettier` and rolldown into a production install.

If image size matters, bundle the server and copy only the output. My [Next.js AI starter kit](https://www.fabwaseem.com/blog/nextjs-ai-starter-kit) uses Drizzle partly for this reason.

## How do migrations compare?

Both generate SQL migration files from your schema. drizzle-kit diffs your TypeScript schema and writes plain SQL, and `push` can sync a dev database without a migration file. Prisma Migrate diffs `schema.prisma`, keeps history in `prisma/migrations` and manages a shadow database for `migrate dev`. Both let you edit the generated SQL before applying it.

Drizzle 1.0 changed the folder layout. `npx drizzle-kit generate --name init` created this:



```text
drizzle/
└── 20261017144733_init/
    ├── migration.sql
    └── snapshot.json
Enter fullscreen mode Exit fullscreen mode

There is no meta/_journal.json any more. The Drizzle v1 upgrade guide says this removes "potential Git conflicts with the journal file," and drizzle-kit generate now runs commutativity checks that flag migrations on two branches that change the same thing. Run npx drizzle-kit up once to convert an existing 0.x migrations folder.

Prisma Migrate is still the more guided tool: prisma migrate dev uses a shadow database to detect drift and offers to reset the dev database when history and schema disagree. One Prisma 7 change to know: seeding no longer runs automatically during migrate dev or migrate reset; you run npx prisma db seed yourself. See the Prisma Migrate docs.

What changed in Drizzle 1.0?

Drizzle 1.0 removes the old relational query API (RQB v1) and replaces it with defineRelations() and RQB v2. Filters and ordering become plain objects. Validators move into the package as drizzle-orm/zod and similar, getTableColumns becomes getColumns, and drizzle-kit drops the migration journal. It also adds MSSQL and CockroachDB dialects.

From the official v0 to v1 changes page:

Area 0.x 1.0
Relations relations() per table One defineRelations() call
Filters where: (u, { eq }) => eq(u.id, 1) where: { id: 1 }
Casing drizzle({ casing: "snake_case" }) snakeCase.table(...)
Validation drizzle-zod package drizzle-orm/zod
Migrations meta/_journal.json One folder per migration
push / pull public schema only by default All schemas by default
Introspection ~10 s under 1 s (Drizzle's figure)

The relations v1 to v2 guide covers the query migration step by step. As of October 17, 2026, npm latest is still 0.45.4 (published October 8) and rc is 1.0.0-rc.4 (June 27).

Should I use Prisma 8 or Prisma 7?

For production today, Prisma 7. Prisma 8 is a rewrite with a new contract file, a new runtime package and a chained query API, and it is still a release candidate. In my test it was also the slowest to start and the heaviest per process. Prisma 8 is worth trying on a branch now; plan the switch after GA.

Note that npm install prisma already installs Prisma 8, so pin prisma@7 and @prisma/client@7 in existing projects. The full migration path, with the contract and the new client, is in my Prisma 8 guide.

Which should I choose for Next.js?

Both work in Next.js 16 server components, route handlers and server actions. Choose Drizzle for Vercel functions, edge routes or small Docker images, where cold start and install size matter. Choose Prisma 7 when a team wants one schema file, generated types and Prisma Migrate's guardrails more than they want SQL control.

Decision chart for Drizzle vs Prisma in 2026: SQL-first, serverless and small images point to Drizzle; schema-first teams and guided migrations point to Prisma 7; Drizzle 1.0 and Prisma 8 wait for GA

Your situation Pick
You think in SQL and want queries that map one-to-one Drizzle
Serverless, edge or tight cold-start budget Drizzle
Small Docker image or function size limit Drizzle
Team wants one schema file and a generated client Prisma 7
Guided migrations with drift detection and a shadow DB Prisma 7
Existing Prisma 6/7 app Stay on Prisma 7, pin it, test 8 on a branch
New app starting this month Drizzle 0.45 or Prisma 7, and plan the move to 1.0 / 8
Paginating nested data Either: use keyset pagination, not deep OFFSET

Auth libraries matter here too. Better Auth 1.7 lists both drizzle-orm (0.45.2+ or 1.0 RC) and Prisma (5 to 7) as supported peers, but not Prisma 8 yet.

Common Drizzle and Prisma mistakes

  • Deep OFFSET with nested relations. Both ORMs build JSON for every skipped row. Use keyset pagination or a second query.
  • Turning on Prisma relationJoins everywhere. In my test it was slower for paginated includes. Measure per query.
  • Unpinned prisma installs. latest is Prisma 8 RC. Pin major versions in Prisma 7 projects.
  • Expecting jit: true to fix slow queries. It speeds up row mapping, not the SQL.
  • Running Drizzle unbundled on serverless. It loads hundreds of module files at startup. Bundle the server output.
  • Upgrading to Drizzle 1.0 without drizzle-kit up. Old migration folders must be converted first.

Related: choosing primary keys for these schemas? In UUIDv7 in PostgreSQL 18 I benchmarked uuidv7() against UUIDv4 and bigint on 10M rows, with tested Prisma and Drizzle setups for a database-side uuidv7() default.

Related: building the API layer with NestJS? My NestJS 12 migration guide covers the v11 to v12 upgrade, Zod validation (28% faster than class-validator in my test) and v11 vs v12 benchmarks.

Key takeaways

  • Drizzle is a SQL-first TypeScript query builder with no code generation; Prisma is a schema-first ORM with a generated client. Both are fully type-safe.
  • On simple queries, all five clients I tested answered within 0.12-0.21 ms of local PostgreSQL, so raw speed rarely decides the choice.
  • Single-query relation loading plus a deep OFFSET was 6-19x slower than two queries, whether it came from Drizzle, Prisma 8 or Prisma 7 with relationJoins. Keyset pagination fixed it.
  • Bundled, Drizzle started in about 35 ms, Prisma 7 in about 100 ms and Prisma 8 RC in about 370 ms.
  • Production installs were 21-30 MB for Drizzle, 329 MB for Prisma 7 and 130 MB for Prisma 8 RC.
  • Drizzle 1.0 and Prisma 8 are both release candidates on October 17, 2026; use Drizzle 0.45 or Prisma 7 in production and pin your versions.

FAQ

Is Drizzle better than Prisma?

Neither is better for everyone. Drizzle suits developers who want SQL-shaped queries, no code generation, small installs and fast serverless cold starts. Prisma suits teams that want one schema file, a generated model client and guided migrations. Both are fully type-safe, and on simple queries their speed was within 0.1 ms in my test.

Is Drizzle faster than Prisma?

Slightly, on most queries. Drizzle 1.0 RC was fastest on lookups, first pages and inserts, but every client stayed between 0.12 and 0.21 ms on simple queries against local PostgreSQL. The bigger differences were cold start (Drizzle about 35 ms bundled vs about 100 ms for Prisma 7) and nested pagination with OFFSET.

Why is my nested query with OFFSET slow?

Drizzle, Prisma 8 and Prisma 7 with relationJoins load relations in one SQL statement, with a JSON subquery per parent row. PostgreSQL applies OFFSET last, so it builds JSON for every skipped row. At OFFSET 960 the posts subquery ran 980 times. Use keyset pagination (WHERE id > lastId) or a second query.

Is Drizzle 1.0 stable?

Not yet. On October 17, 2026, npm latest for drizzle-orm is 0.45.4, published October 8, and the rc tag is 1.0.0-rc.4 from June 27. Drizzle 1.0 replaces relational queries v1 with defineRelations and RQB v2 and changes the migration folder layout, so test it on a branch first.

Should I use Prisma 8 or Prisma 7?

Use Prisma 7 in production for now. Prisma 8 is a release candidate with a new contract file, a new runtime package and a chained query API. In my test it also had the slowest cold start, about 370 ms bundled. Note that npm install prisma already installs Prisma 8, so pin prisma@7 in existing projects.

Which is better for serverless, Drizzle or Prisma?

Drizzle. Bundled with esbuild, importing Drizzle and running the first query took about 35 ms, against about 100 ms for Prisma 7 and about 370 ms for Prisma 8 RC. Drizzle's production install was 21 to 30 MB, while Prisma 7's came to 329 MB with npm ci --omit=dev.

Does Drizzle have migrations like Prisma Migrate?

Yes. drizzle-kit generate diffs your TypeScript schema into SQL migration files, drizzle-kit migrate applies them, and drizzle-kit push syncs a dev database directly. Drizzle 1.0 stores each migration in its own folder with no journal file and checks for conflicting migrations across branches. Prisma Migrate adds a shadow database and drift detection.

Does Better Auth work with Drizzle and Prisma?

Yes, with both. Better Auth 1.7.10 lists drizzle-orm 0.45.2 or newer, including the 1.0 release candidates, and Prisma 5, 6 and 7 as supported peer dependencies. Prisma 8 is not in its supported range yet, so a Better Auth app on Prisma should stay on Prisma 7 for now.


Originally published on fabwaseem.com.

Top comments (0)