# Why Prisma ORM Checks Types Faster Than Drizzle URL: https://www.prisma.io/blog/why-prisma-orm-checks-types-faster-than-drizzle Published: 2025-09-09 Authors: Nikolas Burk, Ankur Datta Tags: education, orm Description: Discover why Prisma ORM outperforms Drizzle ORM in TypeScript type checking. Our benchmarks show Prisma checks queries 72% faster on average, thanks to code generation that keeps editors snappy and CI runs smooth. > [!NOTE] > **New: the Prisma 8 performance benchmark** > > This post is about type-checking performance. On the runtime side, we've now benchmarked [Prisma 8](https://pris.ly/pn-series), the new TypeScript-first foundation for Prisma ORM: it reaches about 87% of the raw `pg` driver's peak throughput and sustains 52% more requests per second than Prisma 7. [See the latest results](https://www.prisma.io/blog/prisma-next-performance-benchmark). Type performance is just as important as runtime speed for developer experience. Slow types mean laggy editors, broken autocomplete, and long CI runs. This post benchmarks Prisma ORM and Drizzle ORM to show how their different approaches affect TypeScript compiler performance. ## What is type performance & why it matters [#what-is-type-performance--why-it-matters] When we think about performance in our apps, we often think about runtime metrics: response times, cold starts, database query speeds. But TypeScript developers know that there's a whole other dimension to performance: *type checking performance*. This is how efficiently the TypeScript compiler can analyze, evaluate, and validate your types during development. And when type performance degrades, developers will feel it immediately through laggy editors or slow CI processes: * Auto-complete takes seconds to load or breaks entirely * Jump-to-definition stops working * `tsc` takes minutes to finish in CI TypeScript is essentially doing *runtime computations at compile time*, especially with modern libraries that lean heavily on advanced type features. When your types become large or deeply recursive, TypeScript's job becomes exponentially harder. The result? A sluggish, frustrating developer experience. ## Prisma ORM vs. Drizzle: Different type system philosophies [#prisma-orm-vs-drizzle-different-type-system-philosophies] In this article, we compare type performance of Prisma ORM and Drizzle ORM. Though both aim to provide type-safe access to your database, they follow fundamentally different philosophies: * **Prisma** **ORM** **uses code generation:** It precomputes types at build time via the `prisma generate` command and writes them to `.d.ts` files. Your editor simply loads these precomputed types. This approach builds on [the Prisma Schema Language, a declarative way to define your data model](https://www.prisma.io/blog/prisma-schema-language-the-best-way-to-define-your-data) that makes type generation possible. * **Drizzle** **ORM** **uses** **type inference**: It defines your schema in plain TypeScript and lets the compiler infer types from it in real time. This happens every time you write a query. Here are the main differences and tradeoffs of the two approaches: | | Code generation (Prisma ORM) | Type inference (Drizzle) | | --------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ | | **When types are created** | During `prisma generate` — written to `.d.ts` files | On the fly — inferred by the TypeScript compiler whenever you write code | | **How the editor/CI sees them** | Loads pre-generated `.d.ts` types | Recomputes types directly from your schema and queries | | **Impact on type-checking performance** | Stable, since most computation happens ahead of time | More variable, since each query triggers fresh inference | ## Methodology: Benchmarking type performance with `@ark/attest` [#methodology-benchmarking-type-performance-with-arkattest] To create our type checking benchmarks, we collaborated with TypeScript expert [David Blass](https://x.com/ssalbdivad), the creator of [ArkType](https://github.com/arktypeio/arktype), a type-safe runtime validation library. David is well known in the TypeScript ecosystem for his full-time open-source work on advancing type system tooling and performance, and he built [`@ark/attest`](https://github.com/arktypeio/arktype), the library we used to test and benchmark type-level logic. The benchmark setup is available in an [open-source repo on GitHub](https://github.com/prisma/type-benchmarks) and has three main ingredients: ### Benchmark framework [#benchmark-framework] `@ark/attest` provides a way to write tests that measure *type instantiations* (the number of types the compiler needs to evaluate) and *check time* (how long it takes to type-check the file). This is more reliable than timing `tsc` runs alone because instantiations are deterministic across environments and TypeScript versions. ### Schema [#schema] We used the classic [Northwind schema](https://github.com/drizzle-team/drizzle-northwind-benchmarks-pg) as a realistic database model. Both Prisma and Drizzle benchmarks were reduced to only the type-relevant expressions so that runtime logic didn't interfere. ### Benchmark files [#benchmark-files] Each ORM was tested in two ways: * **Schema benchmarks** ([`prisma.schema.bench.ts`](https://github.com/prisma/type-benchmarks/blob/main/prisma.schema.bench.ts) and [`drizzle.schema.bench.ts`](https://github.com/prisma/type-benchmarks/blob/main/drizzle.schema.bench.ts)) measured the cost of checking the entire schema at once. * **Query benchmarks** ([`prisma.query.bench.ts`](https://github.com/prisma/type-benchmarks/blob/main/prisma.query.bench.ts), [`drizzle.query.ts`](https://github.com/prisma/type-benchmarks/blob/main/drizzle.query.bench.ts) and [`drizzle.relational.bench.ts`](https://github.com/prisma/type-benchmarks/blob/main/drizzle.relational.bench.ts)) measured the cost of evaluating types in realistic queries. For Drizzle, we included **two variants** because they represent different levels of abstraction: * **Relational Query Builder API (RQB):** A higher-level API ([`drizzle.relational.bench.ts`](https://github.com/prisma/type-benchmarks/blob/main/drizzle.relational.bench.ts)) with relational helpers. This is the closest conceptual equivalent to Prisma Client and the main point of comparison in our analysis. * **SQL Query Builder:** A lower-level builder ([`drizzle.query.ts`](https://github.com/prisma/type-benchmarks/blob/main/drizzle.query.bench.ts)) that maps more directly to SQL. It offers more flexibility but produces more complex type signatures, which typically stress the compiler more. The repository is structured so that each benchmark file contains one or more `attest.bench` blocks. For example, here is a simplified snippet from the Prisma query benchmarks: ```ts title="prisma.query.bench.ts" import { bench } from "@ark/attest"; import { prisma } from "./generated/client"; bench("Customers: getInfo", () => { prisma.customer.findFirst({ select: { id: true, companyName: true, contactName: true }, }); }); bench("Orders: getById", () => { prisma.order.findUnique({ where: { id: 1 }, include: { customer: true, employee: true }, }); }); ``` These are the equivalent snippets from the Drizzle benchmarks: #### Relational ```ts title="drizzle.relational.bench.ts" import { bench } from "@ark/attest"; import { db } from "./db"; import { customers, orders, employees } from "./schema"; bench("Customers: getInfo", () => { db.query.customers.findFirst({ columns: { id: true, companyName: true, contactName: true }, }); }); bench("Orders: getById", () => { db.query.orders.findFirst({ where: (orders, { eq }) => eq(orders.id, 1), with: { customer: true, employee: true }, }); }); ``` #### Query Builder ```ts title="drizzle.query.bench.ts" import { bench } from "@ark/attest"; import { db } from "./db"; import { customers, orders, employees, details } from "./schema"; bench("Customers: getInfo", async () => { await db.select().from(customers).where(eq(customers.id, "id1")); }); bench("Orders: getById", async () => { await db .select({ id: orders.id, shippedDate: orders.shippedDate, shipName: orders.shipName, shipCity: orders.shipCity, shipCountry: orders.shipCountry, productsCount: sql`count(${details.productId})`.as("productsCount"), quantitySum: sql`sum(${details.quantity})`.as("quantitySum"), totalPrice: sql`sum(${details.quantity} * ${details.unitPrice})`.as("totalPrice"), }) .from(orders) .leftJoin(details, eq(orders.id, details.orderId)) .where(eq(orders.id, "id5")) .groupBy(orders.id) .orderBy(asc(orders.id)); }); ``` When the benchmarks are executed, the `@ark/attest` library type-checks each block and records: * **Type instantiations**: The number of triggered type instantiations * **Check time**: The elapsed compiler time (measured on an AWS EC2 `m7i.large` instance) This methodology ensures a proper comparison: same schema, same queries, same TypeScript version, measured through the same framework, leading to the same result metrics. ## Results: Measuring type instantiations and check time [#results-measuring-type-instantiations-and-check-time] We report two sets of results measured on an `m7i.large` instance. The first one compares Prisma ORM with Drizzle 0.44.4 (stable at the time of testing) and remains the primary dataset. The second measures against Drizzle's [recent beta release with improved type-checking performance](https://x.com/DrizzleORM/status/1961533592208625672). ### Prisma ORM vs Drizzle **0.44.4** (main results) [#prisma-orm-vs-drizzle-0444-main-results] #### Schemas [#schemas] **Type instantiations:** | **Prisma ORM** | **Drizzle ORM** | **Difference (count)** | **Difference (%)** | | -------------- | --------------- | ---------------------- | ------------------ | | 428 | 41150 | +40722 | +9514% | **Check time:** | **Prisma ORM** | **Drizzle ORM** | **Difference (ms)** | **Difference (%)** | | -------------- | --------------- | ------------------- | ------------------ | | 205.02ms | 601.58ms | +396.56ms | +193% | #### Queries [#queries] **Type instantiations:** | **Label** | **Prisma ORM** | **Drizzle RQB API** | **Difference (%)** | | ------------------ | -------------- | ------------------- | ------------------ | | Customers: getInfo | 437 | 731 | +67% | | Customers: search | 213 | 341 | +60% | | Employees: getInfo | 1050 | 1710 | +63% | | Suppliers: getInfo | 429 | 697 | +62% | | Products: getInfo | 900 | 1626 | +81% | | Products: search | 213 | 341 | +60% | | Orders: getAll | 1538 | 1441 | -6% | | Orders: getById | 1542 | 1649 | +7% | | Orders: getInfo | 747 | 1950 | +161% | | **Average** | **785** | **1165** | **+48%** | | **Label** | **Prisma ORM** | **Drizzle SQL Query Builder** | **Difference (%)** | | ------------------ | -------------- | ----------------------------- | ------------------ | | Customers: getInfo | 437 | 751 | +72% | | Customers: search | 213 | 701 | +229% | | Employees: getInfo | 1050 | 8448 | +705% | | Suppliers: getInfo | 429 | 699 | +63% | | Products: getInfo | 900 | 1753 | +95% | | Products: search | 213 | 609 | +186% | | Orders: getAll | 1538 | 2179 | +42% | | Orders: getById | 1542 | 2385 | +55% | | Orders: getInfo | 747 | 2671 | +258% | | **Average** | **785** | **2244** | **+186%** | **Check time:** | **Prisma ORM** | **Drizzle RQB API** | **Difference (%)** | | -------------- | ------------------- | ------------------ | | 335ms | 697ms | **+108%** | | **Prisma ORM** | **Drizzle SQL Query Builder** | **Difference (%)** | | -------------- | ----------------------------- | ------------------ | | 335ms | 774ms | **+131%** | ### Prisma ORM vs Drizzle **1.0.0-beta.1-2acab7f** [#prisma-orm-vs-drizzle-100-beta1-2acab7f] Drizzle recently published a beta with type-checking improvements. We re-ran the suite against this version to provide an up-to-date comparison; check times below are the re-measured results from the same machine as above. #### Schemas [#schemas-1] **Type instantiations:** | **Prisma ORM** | **Drizzle ORM** | **Difference (count)** | **Difference (%)** | | -------------- | --------------- | ---------------------- | ------------------ | | 428 | 5017 | +4589 | +1072% | **Check time:** | **Prisma ORM** | **Drizzle ORM** | **Difference (ms)** | **Difference (%)** | | -------------- | --------------- | ------------------- | ------------------ | | 191.18ms | 369.19ms | +178.01ms | +93% | #### Queries [#queries-1] **Type instantiations:** | **Label** | **Prisma ORM** | **Drizzle RQB API** | **Difference (%)** | | ------------------ | -------------- | ------------------- | ------------------ | | Customers: getInfo | 437 | 364 | -17% | | Customers: search | 213 | 282 | +32% | | Employees: getInfo | 1050 | 514 | -51% | | Suppliers: getInfo | 429 | 364 | -15% | | Products: getInfo | 900 | 514 | -43% | | Products: search | 213 | 298 | +40% | | Orders: getAll | 1538 | 955 | -38% | | Orders: getById | 1542 | 1022 | -34% | | Orders: getInfo | 747 | 459 | -39% | | **Average** | **785** | **530** | **-32%** | | **Label** | **Prisma ORM** | **Drizzle SQL Query Builder** | **Difference (%)** | | ------------------ | -------------- | ----------------------------- | ------------------ | | Customers: getInfo | 437 | 883 | +102% | | Customers: search | 213 | 838 | +293% | | Employees: getInfo | 1050 | 2963 | +182% | | Suppliers: getInfo | 429 | 851 | +98% | | Products: getInfo | 900 | 1779 | +98% | | Products: search | 213 | 821 | +285% | | Orders: getAll | 1538 | 2451 | +59% | | Orders: getById | 1542 | 2657 | +72% | | Orders: getInfo | 747 | 2781 | +272% | | **Average** | **785** | **1780** | **+127%** | **Check time:** | **Prisma ORM** | **Drizzle RQB API** | **Difference (%)** | | -------------- | ------------------- | ------------------ | | 284ms | 416ms | **+47%** | | **Prisma ORM** | **Drizzle SQL Query Builder** | **Difference (%)** | | -------------- | ----------------------------- | ------------------ | | 284ms | 519ms | **+83%** | ### Result analysis between Drizzle's Relational Query Builder and Prisma ORM [#result-analysis-between-drizzles-relational-query-builder-and-prisma-orm] For a fair comparison, this analysis looks at Prisma ORM vs. Drizzle's Relational Query Builder (RQB) API, since both provide higher-level relational abstractions. We're also analysing the results of the comparison with Drizzle's beta version since that version performs notable better than the current live version of Drizzle ORM. The schema benchmarks show the biggest difference. Prisma's generated schema types require only a few hundred instantiations, while Drizzle's inferred schemas require more than five thousand. That's because Drizzle asks the compiler to "re-derive everything from first principles" in each build, while Prisma "front-loads" that work during its code generation step. The result is that Prisma's check time is not just *faster*, but also more predictable as schemas grow. In practice, it means that as your app scales, Prisma's type-checking performance remains relatively stable and easy to anticipate, while Drizzle's may degrade in spiky or exponential ways as the schema grows. For queries, Drizzle beta version leads to fewer type instantiations but still has a higher check time, suggesting that type instantiations are not the most important metric and actually measuring the time needed by the compiler remains crucial when evaluating *actual* type checking performance. ## Why Prisma ORM performs better in type checking [#why-prisma-orm-performs-better-in-type-checking] So what accounts for the difference? Several architectural advantages in Prisma lead to better type performance, let's go through them. ### Code generation as precomputation [#code-generation-as-precomputation] Prisma performs type-heavy work once during `prisma generate`. The output `.d.ts` files cache type relationships so the compiler doesn't have to redo them on every keystroke. > Prisma has a big advantage — there's a buildtime step where lots of types are constructed. > > David Blass Drizzle, by contrast, re-derives types each time a query is written. This explains why Prisma's instantiation counts are usually much lower. ### Flatter, cache-friendly types [#flatter-cache-friendly-types] Prisma's generated types avoid deep recursion, prefer interfaces for structural reuse, and minimize conditional unions — all of which align with TypeScript's internal caching model. ### Avoiding type-system footguns [#avoiding-type-system-footguns] By generating types instead of inferring them, Prisma sidesteps common pitfalls like [non-homomorphic mapped types](https://github.com/microsoft/TypeScript/wiki/Performance#writing-easy-to-compile-code), [deeply nested conditional types](https://github.com/microsoft/TypeScript/wiki/Performance#naming-complex-types), or [intersection-heavy unions](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections), patterns known to slow down the TypeScript compiler. ## How we optimized type checking in Prisma ORM [#how-we-optimized-type-checking-in-prisma-orm] As part of our collaboration with David Blass, we also underwent a major optimization effort for our type checking performance. Here's a summary of a few things we did: * **Structural deduplication**: Refactored generated output (e.g. `commonInputTypes`) to reuse interfaces instead of duplicating shapes. Result: file size down 76% (339 → 82 LOC), check time down 45%. * **Homomorphic mapped types**: Rewrote mapped types to preserve optional modifiers, JSDoc, and go-to-definition. This improves both DX and compiler efficiency. * **`readonly` arrays as the base case**: Prevents inference traps where readonly tuples break assignability to `any[]`. * **Simplified abstractions**: Removed unnecessary utilities that mirrored built-ins or introduced extra indirection without benefit. * **Improved input validation patterns**: Consolidated multi-step generic chains into single mapped-type validators for better readability and performance. * **Type performance CI tests**: Added CI guardrails that snapshot instantiations/check times to prevent regressions (PR [#26894](https://github.com/prisma/orm/pull/26894)). If you're curious about the details, you can check out the PRs ([#27775](https://github.com/prisma/orm/pull/27775) and [#27777](https://github.com/prisma/orm/pull/27777)) where we implemented the optimizations. ## Conclusion: Type safety doesn't have to come at the cost of DX [#conclusion-type-safety-doesnt-have-to-come-at-the-cost-of-dx] Prisma ORM and Drizzle ORM both embrace TypeScript, but they take very different routes. Prisma ORM uses code generation to keep types fast and stable. Drizzle ORM leans on type inference for flexibility, but that flexibility can come with performance costs. Our benchmarks showed the following: * **Schemas (Drizzle 0.44.4 vs Prisma ORM)**: Prisma ORM required only a few hundred type instantiations, while Drizzle ORM needed over 40,000 (\~95× difference). As a result, check time was \~2.9x faster with Prisma ORM. * **Queries (Drizzle 0.44.4 RBQ API vs Prisma ORM)**: Prisma ORM averaged 48% fewer type instantiations and had \~2.1× faster check times. Note that Drizzle's 1.0.0 beta significantly reduces schema instantiations to \~5k (+1072% vs Prisma ORM). For queries, Drizzle’s RQB now averages 32% fewer instantiations than Prisma, but check times remain \~1.5× slower. You can explore all the benchmark files in our [open-source repo](https://github.com/prisma/type-benchmarks/blob/main/benchmark.md). If you are building a large, complex app where developer velocity and CI performance matter, Prisma ORM’s approach scales further and faster. Most importantly, type performance is developer experience. The faster your editor responds, the quicker your feedback loop and the happier your developers. If you want to see how that workflow looks in practice, explore [Prisma ORM](https://www.prisma.io/orm), review how [Prisma Client generation works](https://www.prisma.io/docs/orm/v7/prisma-client/setup-and-configuration/generating-prisma-client), and compare it with [TypedSQL](https://www.prisma.io/orm) for the cases where you need lower-level control. > We used Drizzle ORM versions `0.44.4` (stable) and `1.0.0-beta.1-2acab7f` and compared them against Prisma ORM `6.15.0`.