📑 Contents
Overview / TL;DR The Prisma 7 Shift Schema Modeling prisma.config.ts Driver Adapters Migrations Workflow Prisma Client API Client Extensions TypedSQL N+1 & Performance Postgres, Accelerate, Pulse Prisma vs Drizzle vs Kysely Upgrading v6 → v7 When to Use Prisma ReferencesPrisma ORM — Deep Study
Schema, migrations, the type-safe client, and the 2026 rewrite that ditched the Rust query engine for a pure TypeScript/WASM runtime.
1. Overview / TL;DR
Prisma is a TypeScript ORM built around three pieces that work together: a declarative
schema file (schema.prisma) that describes your data model, a
migration engine that turns schema changes into versioned SQL migrations, and a
fully generated, type-safe client that gives you autocomplete for every model,
field, and relation in your database.
What changed most recently: Prisma ORM 7 (November 2025) removed the Rust
query engine that had powered every Prisma Client since v1. Queries now run through a
TypeScript/WebAssembly compiler in-process — no more spawning a separate native binary. This is the
single biggest architectural change in Prisma's history and it changes several things you set up
by hand: driver adapters are now mandatory, config moved to a dedicated prisma.config.ts
file, and the generator provider name itself changed.
| Piece | File / Command | Job |
|---|---|---|
| Schema | prisma/schema.prisma | Single source of truth for models, fields, relations, enums, and datasource config |
| Migrate | prisma migrate dev / deploy | Generates and applies versioned SQL migration files from schema diffs |
| Client | prisma generate → @prisma/client | Type-safe query builder generated straight from your schema |
| Config v7+ | prisma.config.ts | Consolidates schema path, migration settings, seed command, env loading |
2. The Prisma 7 Shift — Rust-Free Architecture
From v1 through v6, every Prisma Client query was serialized, sent to a separate Rust binary (the "query engine"), executed there, and serialized back. This gave Prisma a fast, battle-tested core, but it also meant shipping a multi-megabyte native binary per platform/architecture — a real pain point on serverless and edge runtimes (Vercel Edge, Cloudflare Workers) that don't support native binaries at all.
What changed
- The query engine was rewritten in TypeScript, with a WebAssembly query compiler running directly on the JS main thread — no separate engine process to spawn or communicate with.
- Queries now go straight from Prisma Client to a JavaScript database driver (via a driver adapter) instead of round-tripping through Rust.
- The generator provider changed from
prisma-client-jstoprisma-client, and generated output now defaults to a path in your source tree rather thannode_modules/.prisma.
Measured impact
~14 MB → ~1.6 MB (roughly 90% smaller) — the native Rust binary is gone entirely from the deployed artifact.
Up to 3–3.4x faster query execution, from eliminating cross-language (JS ⇄ Rust) serialization overhead on every call.
Schema type evaluation cut by ~98%, query-evaluation types by ~45%, for roughly a
70% faster full tsc check — the result of a collaboration with the
ArkType author on Prisma's generated types.
No native binary means simpler, smaller deploys to Vercel Edge and Cloudflare Workers, which historically needed workarounds (or Prisma Accelerate) to run Prisma at all.
The Rust-free engine shipped as preview in 6.9.0, reached production-ready status by 6.16.0, and became the default (only) architecture in Prisma ORM 7.0.0.
3. Schema Modeling
Everything starts with schema.prisma — models map to tables, fields map to columns,
and relations are declared on both sides so the client can generate nested types.
generator client {
provider = "prisma-client" // rust-free, ESM by default in v7
output = "./generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
tags Tag[] @relation("PostTags")
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[] @relation("PostTags")
}
enum Role {
USER
ADMIN
}
This one schema encodes all three standard relation shapes:
- One-to-many —
User↔PostviaauthorId, the foreign key +@relation(fields:, references:)pair. - Many-to-many —
Post↔Tagvia a named relation; Prisma manages the join table implicitly unless you model it explicitly. - Enums —
Rolebecomes a native Postgres enum type (or a check-constrained column on databases without enum support).
4. prisma.config.ts new in v7
Settings that used to be scattered across schema.prisma, package.json,
and CLI flags now live in one TypeScript config file at the project root — which also means they
can be computed dynamically (e.g. loaded via dotenv, branched per environment).
import "dotenv/config"
import { defineConfig, env } from "prisma/config"
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "tsx prisma/seed.ts",
},
datasource: {
url: env("DATABASE_URL"),
},
})
Environment variables are no longer auto-loaded from .env by the
CLI in v7 — you now load them explicitly (as above, via dotenv/config) in
prisma.config.ts.
5. Driver Adapters
With the Rust engine gone, Prisma Client no longer talks to the database on its own — it delegates the actual connection to a standard JavaScript database driver, wrapped in a thin driver adapter. This is now a required setup step, not an opt-in preview feature.
| Adapter | Database | Wraps |
|---|---|---|
@prisma/adapter-pg | PostgreSQL | pg (node-postgres) |
@prisma/adapter-ppg | Prisma Postgres | Prisma's own serverless driver |
@prisma/adapter-mariadb | MySQL / MariaDB | mariadb |
@prisma/adapter-better-sqlite3 | SQLite | better-sqlite3 |
@prisma/adapter-libsql | SQLite (Turso) | libSQL |
@prisma/adapter-node-mssql | SQL Server | node-mssql |
| Neon adapter | PostgreSQL (serverless) | Neon's HTTP driver |
| PlanetScale adapter | MySQL (serverless) | PlanetScale WebSocket driver |
| Cloudflare D1 adapter | SQLite (edge) | D1 binding API |
import { PrismaClient } from "./generated/prisma/client"
import { PrismaPg } from "@prisma/adapter-pg"
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL,
})
export const prisma = new PrismaClient({ adapter })
Connection pool behavior now comes from the underlying Node.js driver, not from Prisma's old
Rust-engine pool defaults — if you see new timeout errors after upgrading, tune the pool settings
on the adapter/driver itself (e.g. pg's max, idleTimeoutMillis).
6. Migrations Workflow
| Command | Use case |
|---|---|
prisma migrate dev | Local dev: diff schema vs. database, generate a new SQL migration file, apply it, regenerate the client |
prisma migrate deploy | CI/CD & production: apply pending migrations only — never generates new ones |
prisma migrate reset | Drop the database, reapply all migrations from scratch, re-run seed |
prisma db push | Prototype mode: push schema changes straight to the DB with no migration history — good for early prototyping, not for tracked schema history |
prisma db seed | Run the configured seed script directly |
v7 breaking change: migrate dev no longer auto-seeds the database
and no longer accepts --skip-generate / --skip-seed flags — run
prisma db seed as an explicit separate step.
7. Prisma Client Query API
CRUD basics
// Create
const user = await prisma.user.create({
data: { email: "ada@example.com", name: "Ada" }
})
// Read, with a relation included
const withPosts = await prisma.user.findUnique({
where: { id: user.id },
include: { posts: true }
})
// Filtered read, only selecting some fields
const admins = await prisma.user.findMany({
where: { role: "ADMIN" },
select: { id: true, email: true },
orderBy: { createdAt: "desc" },
take: 20,
skip: 0,
})
// Update
await prisma.user.update({
where: { id: user.id },
data: { name: "Ada Lovelace" }
})
// Delete
await prisma.user.delete({ where: { id: user.id } })
Transactions
The $transaction API batches multiple operations atomically — either every
statement in the array succeeds, or none do:
const [post, updatedUser] = await prisma.$transaction([
prisma.post.create({ data: { title: "Hello", authorId: user.id } }),
prisma.user.update({ where: { id: user.id }, data: { postCount: { increment: 1 } } }),
])
For logic that needs to branch on an intermediate result, use the interactive form instead — pass an async callback that receives a transactional client:
await prisma.$transaction(async (tx) => {
const account = await tx.account.findUnique({ where: { id: fromId } })
if (account.balance < amount) throw new Error("Insufficient funds")
await tx.account.update({ where: { id: fromId }, data: { balance: { decrement: amount } } })
await tx.account.update({ where: { id: toId }, data: { balance: { increment: amount } } })
})
8. Client Extensions ($extends)
The older $use middleware API is deprecated. Client extensions
replace it with a more granular, fully type-safe mechanism split into components: query hooks,
result field/method additions, and client-level method additions.
const prismaWithSoftDelete = prisma.$extends({
query: {
post: {
async delete({ args, query }) {
// turn every post.delete() into a soft-delete update instead
return prisma.post.update({ ...args, data: { deletedAt: new Date() } })
},
},
},
result: {
post: {
excerpt: {
needs: { content: true },
compute(post) {
return post.content?.slice(0, 140) ?? ""
},
},
},
},
})
Query components intercept the lifecycle of a specific model's operations (or all models); result components add computed, type-safe fields to what a query returns; client components add brand-new top-level methods to the extended client instance.
9. TypedSQL — Raw SQL With Type Safety
Not every query maps cleanly to the fluent API — window functions, recursive CTEs, or
vendor-specific SQL often need to be hand-written. TypedSQL lets you keep raw .sql
files under prisma/sql/ and get generated, fully-typed functions for them.
-- @param {Int} $1:limit
SELECT u.id, u.name, COUNT(p.id) AS post_count
FROM "User" u
JOIN "Post" p ON p."authorId" = u.id
GROUP BY u.id
ORDER BY post_count DESC
LIMIT $1;
import { getTopAuthors } from "./generated/prisma/sql"
const topAuthors = await prisma.$queryRawTyped(getTopAuthors(10))
// topAuthors is fully typed: { id: number, name: string, post_count: bigint }[]
For ad-hoc parameterized SQL without a dedicated file, Prisma.sql is the
safe-templating helper — it's what client-extension query components typically wrap raw SQL
in to avoid string-concatenation injection risk.
10. N+1 Queries & Performance
The classic ORM footgun still applies: fetching a list, then lazily fetching each item's relation in a loop, produces N+1 round trips instead of one.
for (const u of users) { u.posts = await prisma.post.findMany({ where: { authorId: u.id } }) }
— one query per user, plus the original list query.
prisma.user.findMany({ include: { posts: true } }) — Prisma batches this into
efficient joined/batched SQL under the hood in one round trip.
Beyond query shape, the v7 rewrite itself is the biggest recent performance lever — removing the JS↔Rust serialization step cuts per-query overhead independent of how the query is written.
11. Prisma Postgres, Accelerate & Pulse
Beyond the open-source ORM, Prisma ships a hosted data platform with three pieces that solve different problems:
A managed, serverless Postgres offering with no cold starts, provisioned via
npm create db. As of v7 it also speaks the standard Postgres wire protocol, so
it works with generic tools (TablePlus, Retool, Cloudflare Hyperdrive) — not just Prisma Client.
A global connection-pooling + query-caching layer, useful when a serverless/edge function
would otherwise open (and exhaust) too many direct database connections. Uses a distinct
prisma:// / prisma+postgres:// connection string.
Change-data-capture as an API: subscribe to inserts/updates/deletes on a table and react to them in real time, without standing up your own logical-replication listener.
Don't mix connection strings: a driver adapter like PrismaPg expects a plain
Postgres connection string and will fail if handed a prisma:// or
prisma+postgres:// Accelerate URL — Accelerate is applied as a separate client
extension on top, not as a driver adapter.
12. Prisma vs Drizzle vs Kysely
- Schema-first (DSL, not SQL)
- Full migration engine built in
- Generated client + Prisma Studio GUI
- Heaviest tooling, most "batteries included"
- TypeScript-first schema (code, not DSL)
- SQL-like query builder, thinner abstraction
- No generated client step — types come straight from your schema code
- Smaller runtime footprint, closer to raw SQL
- Not an ORM — a type-safe SQL query builder
- You own the schema/migrations tooling separately
- Maximum control over generated SQL
- Best fit when you want Prisma-level type safety but zero abstraction magic
Prisma's pitch is developer experience and batteries-included tooling (schema DSL, migrations, Studio); Drizzle and Kysely trade some of that convenience for a thinner runtime and SQL that stays closer to what actually gets sent to the database.
13. Upgrading from v6 to v7
| Change | Action needed |
|---|---|
| Generator provider | prisma-client-js → prisma-client, add explicit output path |
| Client import path | Update to the new generated output location instead of @prisma/client |
| Driver adapters | Now required for every database — install and wire up the matching @prisma/adapter-* package |
| Config file | Add prisma.config.ts; move schema path, migration settings, seed command there |
| Env loading | .env is no longer auto-loaded — load explicitly via dotenv/config |
| Seeding | Auto-seed on migrate dev removed — run prisma db seed manually |
Middleware ($use) | Removed — port logic to Client Extensions ($extends) |
| Metrics preview feature | Removed entirely |
| Node.js / TypeScript | Minimum Node 20.19.0 (22.x recommended), TypeScript 5.4.0+ (5.9.x recommended) |
npm install @prisma/client@7 npm install -D prisma@7
14. When to Use Prisma
You want a schema-first workflow with a built-in migration history, strong autocomplete without hand-writing types, a GUI (Prisma Studio) for poking at data, and you're fine with a generated-client build step in your toolchain.
You want the schema and types to live directly in TypeScript with no codegen step, need the smallest possible runtime, or want SQL output that stays maximally close to what you wrote.
Connection-pool tuning (now driver-owned, not Prisma-owned), and any code still using
$use middleware or relying on auto-seeding / auto-loaded .env.
The v7 rewrite plus driver adapters (Neon HTTP, PlanetScale WebSocket, D1) makes Prisma meaningfully more edge-friendly than it was pre-2026 — worth revisiting if you ruled it out for Workers/Edge Functions in the past.
References
-
1Announcing Prisma ORM 7.0.0The official v7 release announcement — architecture rewrite, performance numbers, new config file.
-
2Upgrade Guide: Prisma ORM v6 → v7Concrete migration steps, breaking changes table, and code before/after diffs.
-
3Rust-Free Prisma ORM is Ready for ProductionThe production-readiness announcement for the TypeScript/WASM engine ahead of v7.
-
4No Rust Engine — Prisma DocsReference docs for the rust-free client architecture and driver adapter requirement.
-
5Database Drivers — Prisma DocsFull list of official driver adapters per database, including serverless/edge options.
-
6Prisma Client Extensions — Prisma DocsThe $extends API that replaces deprecated $use middleware: query, result, and client components.
-
7Writing Type-safe SQL with TypedSQLHow to author .sql files under prisma/sql and get generated, fully-typed query functions.
-
8Prisma ORM Release Notes / ChangelogVersion-by-version changelog, including the 6.9 preview → 6.16 production → 7.0 default timeline.
-
9prisma/prisma — GitHub ReleasesPrimary-source release history for exact version numbers and dates.
-
10Support for Serverless Database Drivers Is Now in PreviewBackground on the driver-adapter model that later became mandatory in v7, including Neon and PlanetScale support.