On January 20, 2026, Prisma 7 shipped with its headline feature being a deletion. The Rust query engine that had powered the ORM since its rewrite is gone by default. The part that should make you stop scrolling is the benchmark direction: removing the Rust ran the queries faster. Up to 3.4x faster on the common reads, smaller by 90% on disk, and finally light enough to run on the edge.

Rust is supposed to be the fast part. A team rips out the systems language, replaces it with TypeScript and a sliver of WebAssembly, and the numbers go up. That inversion is the actual story, and it is not really about Prisma.

The engine that was never really yours

For most of its life, Prisma did not generate SQL in JavaScript. It shipped a separate query engine written in Rust, distributed as a platform-specific native binary, and your PrismaClient was a thin TypeScript wrapper that talked to it. You wrote a type-safe query, the engine turned it into SQL, ran it against your database, and handed the rows back across the language boundary.

This was a deliberate bet. One engine, written once in a fast language, could back clients in TypeScript, Go, and beyond. The cost showed up everywhere else. The binary had to match your OS and CPU architecture, so installs downloaded the right build and CI caches bloated. The generated client weighed roughly 14 MB once the engine binaries were counted. On serverless, the cold-start penalty of loading that engine was a recurring complaint. On Cloudflare Workers and other edge runtimes, a native binary was simply a non-starter for years.

Prisma named the trade-off plainly when it announced the direction. As Nikolas Burk put it, "TypeScript is the dominant language for building web applications and the goal of supporting multiple languages has become secondary to providing the best DX possible for TypeScript developers."

Translation: the polyglot dream was costing the majority audience more than it returned. So they went looking for where the time actually went.

The friction was operational before it was ever about speed. The engine binary had to be fetched at install time and version-locked to the client that drove it, so a mismatch surfaced as a runtime error rather than a type error. Locked-down or air-gapped CI environments that blocked the download had to mirror the binaries by hand. None of that is a query benchmark, and all of it is the kind of papercut that wears teams down over a year.

Where the time actually went

Intuition says SQL generation and execution dominate a query's cost, and that a Rust engine would crush a TypeScript one at that job. The profiling said something else. The expensive part was the boundary itself.

Every query crossed from JavaScript into the engine and every result set crossed back. That round trip meant serializing the query into the engine's protocol, then deserializing the rows into JavaScript objects on the way out. For a single-row lookup the tax is invisible. For a relational read that pulls thousands of rows with nested include clauses, the serialize-deserialize work grows with the payload and starts to dominate the wall-clock time. Rust generating SQL in 1ms does not help when rehydrating the answer across the boundary costs 200.

Prisma published before-and-after numbers against the same workloads. The pattern is consistent: the heavier and more relational the read, the bigger the win.

findMany workloadRust engineTypeScript + WASMSpeedup
All rows (~25k)163 ms77 ms2.12x
include cast, m2m, take 2k1539 ms136 ms11.32x
where + include cast, m2m, take 2k82 ms38 ms2.16x
include cast + person, where, take 2k169 ms70 ms2.41x

The 11.32x row is the headline-grabber, and it deserves an asterisk. It is a specific many-to-many read with a large nested payload, exactly the shape where boundary serialization was worst. The honest summary is the one Prisma leads with: up to 3.4x faster, with typical reads landing in the 2x to 2.5x range. Some in the community asked whether the gains hold across every workload, and that skepticism is fair. Write-heavy and single-row paths see far less movement, because there was never much payload to serialize.

Two-panel diagram showing data centers merging to streamline data flow

What the TypeScript query compiler actually does

The replacement is a query compiler that lives in TypeScript with a small WebAssembly core for the heavy lifting. It takes the same Prisma query you already write and compiles it to SQL in-process. There is no separate binary, no child process, and no language boundary for results to cross. Rows come back as JavaScript objects without a translation layer in between.

The split between TypeScript and WASM is the interesting design choice. The orchestration, the part that runs on every call, is plain TypeScript so it stays in the runtime with your data and never marshals across a boundary. The denser compilation logic that turns a Prisma query into SQL ships as a WASM module. WASM is portable in a way a per-platform Rust binary is not: one artifact runs in Node, in an isolate, in Deno, without a matrix of OS and CPU builds to download. The expensive boundary crossing on every result set is gone, and the slower-to-rewrite compilation core is preserved as portable bytecode rather than native code bound to a machine.

The visible cost of this is that Prisma no longer owns the database connection. You bring a driver adapter that wraps a real JavaScript database driver. For Postgres that is @prisma/adapter-pg; for SQLite, @prisma/adapter-better-sqlite3; serverless Postgres and other targets get their own adapters. The client is constructed with the adapter rather than a bare connection string buried in config:

import { PrismaClient } from './generated/prisma'
import { PrismaPg } from '@prisma/adapter-pg'

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })
const prisma = new PrismaClient({ adapter })

During the transition releases this lived behind two preview flags in the generator block. If you adopted it early on 6.x, the schema looked like this:

generator client {
  provider        = "prisma-client"
  previewFeatures = ["queryCompiler", "driverAdapters"]
  output          = "../generated/prisma"
}

The rust-free path entered preview in Prisma 6.9.0, reached production readiness around 6.16, and became the default in 7.0, at which point the flags fold away. Early preview supported only Postgres and SQLite; by the 7.0 release the matrix had widened to PostgreSQL, MySQL, SQLite, MongoDB, SQL Server, and CockroachDB.

The migration nobody benchmarks

A 90% bundle cut is easy to celebrate and easy to underestimate the cost of reaching. Going rust-free is a behavioral change in how your app connects and builds, not a silent internal optimization. Three pieces of it will touch real code.

Start with what does not move, because it bounds the work. The query API is untouched. Your findMany, create, and transaction calls read exactly as before, your schema models stay put, and migrations run the same. The change is concentrated at two seams: how the client is constructed and how it reaches the database. For most codebases that means a handful of edited files around client setup, not a rewrite of every query call site. The migration is shallow in surface area and deep in consequence, which is the awkward combination that makes it easy to schedule badly.

Connections move into your hands. With the engine gone, Prisma does not manage the pool. The driver adapter does, which means connection limits, pooling, and timeouts are now decisions you make through the underlying driver. That is more control and more rope. On Postgres especially, you choose between a direct pg pool and a serverless-friendly adapter, the same calculus teams already weigh when they read about Postgres I/O behavior.

Generated code leaves node_modules. By default 7.0 emits the client into your project source rather than hiding it in node_modules/.prisma. That makes the output visible to file watchers and bundlers, which react immediately after a schema change, but it also forces a decision about whether the generated directory is committed or gitignored, and it makes client generation an explicit build step rather than a postinstall side effect.

Configuration splits in two. A dedicated config file separates project configuration from the data schema and lets you compute settings dynamically in JavaScript or TypeScript. Stack the routine breaking changes on top: the deprecated Metrics preview is removed, the generator provider renames from prisma-client-js to prisma-client, and the minimum Node.js and TypeScript versions move up.

ConcernRust era (<= 6.x default)Rust-free (7.0)
Query engineRust native binaryTypeScript + WASM compiler
DB connectionEngine-managedDriver adapter you supply
Generated clientnode_modules/.prismaProject source by default
Generator providerprisma-client-jsprisma-client
Client size~14 MB~1.6 MB
Edge / serverlessPainful or unsupportedFirst-class

There is a second performance win that never shows up in query benchmarks. Prisma reports the schema now needs 98% fewer types to evaluate and full type checks run about 70% faster. Anyone who has watched a large Prisma schema melt their editor's language server will feel that more often than they feel a faster findMany. It lands in the same territory as the broader compiler-speed push behind the native TypeScript compiler.

Isometric 3D software unpacking into small glass cube illustration-edited

The edge was the real prize

The query speedups are nice. The deployment story is what changes architectures. A 1.6 MB client built from JavaScript and WASM, with no native binary attached, runs where a 14 MB engine binary never could.

That list now reads: Cloudflare Workers on workerd, Deno, Bun, Vercel Edge, and ordinary Node. For a long time, putting Prisma on Workers meant routing through a proxy, because the runtime forbids native binaries and arbitrary TCP. With the engine gone and a workerd-compatible driver adapter in place, the client loads in the isolate directly. Teams already running on Bun or Node get the same client either way, which removes one of the last reasons Prisma users felt pinned to a long-lived Node server.

The catch is the one every serverless database story eventually hits: connections. A pool of long-lived connections assumes a long-lived process, and an isolate that spins up per request has neither. Owning the adapter is what makes this solvable rather than hidden. You point it at a connection pooler, or reach for a serverless-oriented driver that multiplexes over HTTP instead of raw TCP, and you size limits to the platform's concurrency rather than to a single server's. Prisma handing you the connection is not only more rope. On the edge it is the only way the rope reaches the dock.

The lesson Rust-in-JS keeps teaching

It is tempting to read this as a verdict on Rust. It is not. The same stretch that saw Prisma remove Rust to get faster also saw Vite move its bundler to Rust and Bun double down on a native core. Both got faster doing it. The contradiction dissolves once you look at how often each design crosses the language boundary.

A bundler hands Rust a pile of files and gets back a bundle. The boundary is crossed a handful of times for an enormous amount of work, so native speed wins outright and the marshaling cost rounds to nothing. An ORM crosses the boundary on every query and drags a result set back across it each time. The work per crossing is small and the crossings are constant, so the marshaling tax compounds until it swamps whatever the native code saved.

Native speed pays off when you cross the language boundary rarely and do heavy work in between. It backfires when you cross constantly and the payload is the point.

That is the heuristic worth keeping. Before reaching for a faster language inside a TypeScript system, count the boundary crossings per unit of real work. Chunky and rare favors native. Chatty and per-operation favors staying in-process, even in the slower language. The same reasoning sits underneath the wider push to collapse query tooling into one process, the one that also produced Drizzle's thin query-builder approach, which never paid the boundary tax because it never built a boundary.

Prisma 7 is a real performance release. It is also a reminder that architecture, not language, sets the ceiling. The question to carry forward is not whether Rust is fast. It is where, in your own stack, you are paying to cross a line you did not need to draw.