Same users and posts shape, local SQLite, pinned versions. The minified bundle is the wide gap. Cold start depends on whether you time the source files or the bundle.
On this page
The short answer
The drizzle vs prisma choice for a small TypeScript app, measured here on one schema, is a bundle-size win for Drizzle. The minified Node bundle was 71,691 bytes for Drizzle and 4,679,321 bytes for Prisma. Running that bundle from a fresh process took a median 38.7 ms with Drizzle and 125.8 ms with Prisma. The unbundled source files went the other way: 247.9 ms and 156.2 ms.
How I tested
People also search prisma vs drizzle. This is that comparison, run on this VM on October 30, 2026, with versions pinned in the script. It lives on the backend and stack hub. The reader is picking a TypeScript ORM for a small app: two tables, one insert, one read that brings the child rows back.
The schema is a user and that user’s posts. Drizzle names the tables users and posts and the foreign key user_id, because those are the strings in the schema file. Prisma, with no @map and no @@map, wrote User, Post, and userId. Same columns, same one-to-many, different default identifiers. Both databases are local SQLite files. There is no Postgres server on this VM, and no hosted database.
npm view during the run said drizzle-orm latest was 0.45.4 and the rc tag was 1.0.0-rc.5. drizzle-kit latest was 0.31.11. The relations docs and the SQLite docs, read the same day, tell you to install drizzle-orm@rc. This benchmark installed the latest tag, 0.45.4, and called relations() from that package. It did not time the release candidate.
npm view prisma dist-tags the same morning had latest at 8.0.0-rc.28 and prev at 7.10.1. The release status page, last updated October 29, 2026 on that page, says Prisma ORM 8 is still a release candidate and that npm install prisma no longer reads a Prisma 7 schema.prisma. This test installed prisma@7.10.1, @prisma/client@7.10.1, and @prisma/adapter-better-sqlite3@7.10.1. It did not install Prisma ORM 8.
The Drizzle app declared better-sqlite3@13.0.3. The Prisma adapter depends on better-sqlite3 ^12.6.0 and npm nested 12.11.1 inside the adapter. Node resolved 13.0.3 from the Drizzle app and 12.11.1 from the adapter. The top-level Prisma app also has 13.0.3, and that copy is in the install size, and it is not the copy the adapter loaded.
esbuild was 0.28.2, installed in a third directory so it is not inside either ORM’s node_modules. The bundle command was minify, platform node, format ESM, target es2022, better-sqlite3 external, plus a one-line banner that defines require through createRequire. The Prisma client bundle needs that banner. The Drizzle bundle got the same flags. Gzip is Node’s zlib.gzipSync at the default compression level.
Two clocks, eleven fresh processes each, median then the slowest and fastest in parentheses. performance.now() is milliseconds since the process started. Import time is the first line of the handler, after the static imports. Query time is one insert of a user, one insert of a post, and one relational read. Total is those two added. Source means node run.mjs or node run.ts (Node 22.23.4 strips types without a flag). Bundle means node dist/run.mjs.
The machine, from the files the script read: 4 CPUs, model name Intel(R) Xeon(R) Processor, kernel 6.12.94+, MemTotal 16,387,280 kB. Node v22.23.4, npm 10.9.9. The run started at 09:17:13 UTC and finished at 09:17:34 UTC. npm’s cache was already warm from earlier attempts the same morning. The sizes below are du -sb of the tree that install produced, not a stopwatch on the download.
The script is scripts/benchmarks/drizzle-vs-prisma/bench.mjs. From that directory, node bench.mjs rewrites results.json and results.csv next to the script. Work files, including node_modules, go to /tmp/drizzle-vs-prisma-bench unless you set BENCH_WORK. A rerun will not match these medians. The rows from this run are the benchmark CSV.
Bundle size and install footprint
Every byte in this section is from that run. du -sb is apparent size, the kind of count that does not change with the disk’s block size.
| Measurement | Drizzle | Prisma |
|---|---|---|
| node_modules after npm install --omit=dev | 38,245,453 bytes | 349,330,174 bytes |
| node_modules after the CLI package is requested too | 81,954,366 bytes | 349,330,174 bytes, then 349,330,587 after prisma generate |
| Generated client on disk | None. The schema file is the client. | 139,403 bytes, 10 files |
| Minified bundle, better-sqlite3 external | 71,691 bytes | 4,679,321 bytes |
| Gzip of that file | 19,466 bytes | 1,656,726 bytes |
-
71,691
Drizzle minified bundle, bytes
-
4,679,321
Prisma minified bundle, bytes
-
19,466
Drizzle gzip, bytes
-
1,656,726
Prisma gzip, bytes
4,679,321 divided by 71,691 is 65.3, to one decimal. The largest esbuild input on the Prisma side was query_compiler_fast_bg.sqlite.wasm-base64.mjs at 4,406,173 bytes. That one file is most of the bundle. Drizzle’s largest input was sqlite-core/dialect.js at 25,717 bytes. esbuild reported 114 inputs for Drizzle and 13 for Prisma.
The install gap is the same shape, and it is not the query compiler alone. On the Drizzle side, drizzle-orm was 10,517,974 bytes and better-sqlite3 was 27,302,969 bytes. That driver ships prebuilds for eight platforms, which is why a native SQLite package is most of a 38,245,453 byte tree. Adding drizzle-kit 0.31.11 lifted the tree to 81,954,366 bytes. The kit folder itself is 10,267,562 bytes. The rest of the jump is packages the kit pulls in, including its own esbuild.
On the Prisma side, npm install --omit=dev and a following install that also requested the prisma devDependency measured the same 349,330,174 bytes. npm explain prisma on that tree shows prisma@7.10.1 as the project’s devDependency and as a peerOptional dependency of @prisma/client. npm installed it either way. prisma generate then moved the tree to 349,330,587 bytes, 413 bytes more. The largest folders were @prisma/client 74,458,622, @prisma/studio-core 44,071,113, prisma 42,043,752, effect 27,089,203, better-sqlite3 27,302,969, @prisma/engines 24,839,621, @electric-sql/pglite 23,262,891, and @prisma/dev 19,021,598. The adapter folder is 12,562,434 bytes, and 12,506,889 of those are the nested better-sqlite3 12.11.1.
Drizzle vs Prisma performance
These times are a local measurement on the VM above. They are not a hosted database, not a Worker, and not a warm server that already imported the ORM. Eleven runs, all of them counted. Medians are rounded to 0.1 ms. The CSV has the unrounded values.
| Clock | Drizzle | Prisma |
|---|---|---|
| Source file, import | 235.6 ms | 83.4 ms |
| Source file, insert plus relational read | 11.4 ms | 72.8 ms |
| Source file, process start through the read | 247.9 ms (240.6 to 253.0) | 156.2 ms (148.4 to 173.8) |
| Minified bundle, import | 24.9 ms | 60.5 ms |
| Minified bundle, insert plus relational read | 12.9 ms | 64.8 ms |
| Minified bundle, process start through the read | 38.7 ms (34.8 to 40.6) | 125.8 ms (124.2 to 130.5) |
Read the two clocks separately. The source handler pays for walking node_modules. Drizzle’s import median was 235.6 ms and the query after that was 11.4 ms, so almost all of the 247.9 ms was the import. Prisma’s source import was 83.4 ms and the query was 72.8 ms. On that clock Prisma finished first.
The bundle is one file, with better-sqlite3 still loaded from disk at runtime. Drizzle’s bundle median was 38.7 ms, and the eleven runs sat between 34.8 ms and 40.6 ms. Prisma’s bundle median was 125.8 ms, between 124.2 ms and 130.5 ms. The query portion stayed in the same neighborhood as the source query: about 13 ms for Drizzle and about 65 ms for Prisma. The thing that moved was the import. Bundling Drizzle removed about 210 ms of import. Bundling Prisma removed about 23 ms of import and left a 4,679,321 byte file to parse.
Each run returned one user and one post. A run that returned anything else would have failed the script. The driver versions are not the same, as the method section says, so the query gap is Drizzle 0.45.4 on better-sqlite3 13.0.3 against Prisma 7.10.1 on better-sqlite3 12.11.1.
Schema and drizzle orm relations
The drizzle orm vs prisma difference in this file is where the relation is written. Drizzle 0.45.4 takes a relations() helper beside the tables. Prisma puts the relation on the model. Both query files then do the same three steps. The script counted lines with a trailing newline stripped: Drizzle’s schema is 23 lines (19 non-empty) and the query file is 11 lines (10 non-empty). Prisma’s schema is 23 lines (20 non-empty) and the query file is 12 lines (12 non-empty). Prisma also has prisma.config.ts, 11 lines in the script’s count, because Prisma 7 reads the SQLite URL from that file rather than from the schema.
import { relations } from 'drizzle-orm';
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
email: text('email').notNull().unique(),
name: text('name').notNull(),
});
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
title: text('title').notNull(),
body: text('body').notNull(),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.userId], references: [users.id] }),
}));import { eq } from 'drizzle-orm';
import { posts, users } from './schema.mjs';
export function run(db, email) {
const user = db.insert(users).values({ email, name: 'Ada' }).returning().get();
db.insert(posts).values({ userId: user.id, title: 'Hello', body: 'First post' }).run();
return db.query.users.findFirst({
where: eq(users.email, email),
with: { posts: true },
});
}generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "sqlite"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
userId Int
title String
body String
user User @relation(fields: [userId], references: [id])
}export async function run(prisma, email) {
const user = await prisma.user.create({
data: { email, name: "Ada" },
});
await prisma.post.create({
data: { userId: user.id, title: "Hello", body: "First post" },
});
return prisma.user.findFirst({
where: { email },
include: { posts: true },
});
}The relations doc currently opens with npm i drizzle-orm@rc and defineRelations(). That is the release candidate’s API. The 0.45.4 package this script installed still exports relations(), and drizzle-kit pull on this run wrote relations() too, imported from drizzle-orm/relations. If you follow the docs and install the RC, you are on a different API than the one timed here.
Migrations: drizzle kit vs Prisma Migrate
Drizzle’s command was npx drizzle-kit generate --config drizzle.config.ts --name init. Without --config, this 0.31.11 binary looked for drizzle.config.json when only an .mjs config existed, and it found drizzle.config.ts once that filename was what sat in the directory. The command wrote drizzle/0000_init.sql, 15 lines, all non-empty, including two --> statement-breakpoint lines. It creates posts before users. The foreign key says ON UPDATE no action ON DELETE no action.
CREATE TABLE `posts` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` integer NOT NULL,
`title` text NOT NULL,
`body` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `users` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`email` text NOT NULL,
`name` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);Applying it is drizzle orm migrations: migrate() from drizzle-orm/better-sqlite3/migrator, after pragma foreign_keys = ON. That is apply.mjs in the script. The cold runs then inserted a post, so the tables were there.
Prisma’s command was npx prisma migrate dev --name init, with CI=1. It created and applied prisma/migrations/20261030091729_init/migration.sql, 18 lines, 16 of them non-empty. prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script printed the same SQL. The applied file creates User, then Post, with ON DELETE RESTRICT ON UPDATE CASCADE, then a unique index on email. A rerun will pick a new timestamp in the folder name.
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT NOT NULL,
"name" TEXT NOT NULL
);
-- CreateTable
CREATE TABLE "Post" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"userId" INTEGER NOT NULL,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
CONSTRAINT "Post_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");prisma generate wrote the client to generated/prisma in 22 ms, according to the CLI’s own line. Ten TypeScript files, 139,403 bytes. The handler imports ./generated/prisma/client.ts.
drizzle-kit pull --dialect sqlite against that Prisma file wrote schema.ts, relations.ts, and 0000_watery_lionheart.sql. The SQL is commented out. The schema includes _prisma_migrations as well as User and Post. relations.ts is the relations() helper, 13 lines in the stored file. I did not run the commented SQL. Pull’s filename is a random pair of words. The next run will not be watery_lionheart.
Postgres hosts
This section is docs, not a timing. psql is not installed here, and the script never opened a server. What a local Postgres socket costs, next to a SQLite file, is the measurement in SQLite vs Postgres. What Neon and Supabase charge, and what happens when the database goes quiet, is Neon vs Supabase. If the other hosted database on the list is Firestore, the bill at 1,000, 10,000, and 50,000 users is in Supabase vs Firebase.
A neon drizzle orm connection is a different driver from the SQLite one above. drizzle-orm 0.45.4, the package this script installed, exports drizzle-orm/neon-http and drizzle-orm/neon-serverless. The Neon driver page, read October 30, 2026, shows drizzle-orm/neon-http with @neondatabase/serverless, and the install snippet on that page uses drizzle-orm@rc. This test did not install @neondatabase/serverless and did not open a Neon URL. Prisma’s PostgreSQL connector, read the same day, shows a Neon serverless adapter constructed as PrismaNeon with a connection string. This test did not install that adapter.
The SQLite numbers do not transfer to either host. A hosted Postgres adds a network round trip this run never measured.
Which to pick
- The shipped JavaScript has to stay small, and the native driver can stay outside the bundle: Drizzle. 71,691 bytes minified against 4,679,321, and a bundle cold median of 38.7 ms against 125.8 ms on this VM.
- You want
prisma migrate dev, a generated client, and the install size is acceptable: Prisma 7.10.1. The tree was 349,330,174 bytes even with--omit=dev, because npm still installed the CLI. - You time the unbundled source, the way
node run.tsran here: Prisma finished the cold process first, 156.2 ms median against 247.9 ms. Almost all of Drizzle’s source time was the import. - The process stays up and the query is the hot path: Drizzle’s query median was 11.4 ms in the source runs and 12.9 ms in the bundle runs. Prisma’s was 72.8 ms and 64.8 ms. The drivers were better-sqlite3 13.0.3 and 12.11.1.
- You already have Prisma tables in SQLite and want a Drizzle schema file:
drizzle-kit pullwrote one, including_prisma_migrations. The queries are still a rewrite. This post did not run one. - The database is Neon or Supabase: pick the host in Neon vs Supabase, then point the ORM at it. The timings above are a local file.
| Situation | Pick | Why, from this run |
|---|---|---|
| Minified bundle and bundle cold start | Drizzle 0.45.4 | 71,691 bytes and 38.7 ms median, against 4,679,321 bytes and 125.8 ms |
| Unbundled source cold start | Prisma 7.10.1 | 156.2 ms median against 247.9 ms. The import was 83.4 ms against 235.6 ms |
| Install footprint | Drizzle | 38,245,453 bytes runtime, 81,954,366 with drizzle-kit. Prisma was 349,330,174 and still had the CLI |
| Migration command | Either, they differ | drizzle-kit wrote 15 lines and named user_id. prisma migrate dev wrote 18 lines and named userId |
| Moving an existing Prisma SQLite file | drizzle-kit pull, then rewrite queries | pull wrote schema.ts and relations.ts. The SQL it wrote is commented out and was not executed |
That ORM sits on a database you also have to host. SQLite vs Postgres is the local file against a local server. Neon vs Supabase is the hosted Postgres bill.
SQLite vs Postgres, timed on one machine 100,000 inserts, a dashboard query, and 1, 4, and 8 writers, measured locally. Blog Neon vs Supabase: free tier, price, and branching Free caps, the first paid month, branching, and what happens when the database goes quiet. BlogFAQ
Is Drizzle faster than Prisma?
On this VM, for this one insert and one relational select, it depends which file you start. The minified bundle’s median from process start through the select was 38.7 ms for Drizzle and 125.8 ms for Prisma, eleven runs. The unbundled source was 247.9 ms for Drizzle and 156.2 ms for Prisma. After imports, the query itself was about 11 to 13 ms for Drizzle and about 65 to 73 ms for Prisma. Drizzle loaded better-sqlite3 13.0.3. Prisma’s adapter loaded better-sqlite3 12.11.1. The runs are in the CSV.
Is Drizzle better than Prisma for serverless and edge?
This test did not deploy either ORM. The measured bundle is a Node build with better-sqlite3 left external, so the native addon is not inside the file and this artifact is not a Worker by itself. The installed drizzle-orm 0.45.4 exports drizzle-orm/neon-http. Drizzle’s Neon page, read October 30, 2026, documents that driver with @neondatabase/serverless for HTTP from serverless. This test did not install that package. Prisma 7 requires a driver adapter, and the driver-adapter docs say that is what makes an edge deployment possible. This test did not time one. The host bill for Neon and Supabase is in Neon vs Supabase.
Can you migrate from Prisma to Drizzle?
The tables, partly. drizzle-kit pull against the SQLite file Prisma had just migrated wrote schema.ts, relations.ts, and a commented SQL file named 0000_watery_lionheart.sql. The pull saw _prisma_migrations, User, and Post. relations.ts uses relations() from drizzle-orm/relations. I did not execute that SQL, and I did not run a tool that rewrites prisma.user.create into a Drizzle insert. A rerun of pull can pick a different filename.
Does Prisma still ship a query engine binary?
Not as a query-engine file, on the Prisma ORM 7.10.1 install measured October 30, 2026. A search for files named query-engine or libquery_engine found none. prisma generate wrote 10 TypeScript files, 139,403 bytes, under generated/prisma. The bundle’s largest input was query_compiler_fast_bg.sqlite.wasm-base64.mjs at 4,406,173 bytes. The install did contain a schema engine: schema-engine-debian-openssl-3.0.x at 23,516,472 bytes, and schema_engine_bg.wasm at 5,406,029 bytes. The November 19, 2025 changelog says the Rust-free client is the default in v7 and that BinaryEngine and LibraryEngine were removed. The v7 generators page, last updated September 11, 2026 on that page, still says generate includes the query engine binary. The files on disk matched the changelog.
Drizzle Studio vs Prisma Studio?
I did not open either GUI. Drizzle’s studio docs, read October 30, 2026, say drizzle-kit studio serves a local browser on 127.0.0.1:4983, that local Studio is free, and that Studio is not open source. Prisma’s Studio docs, last updated September 11, 2026 on the page and read October 30, 2026, say Prisma Studio browses PostgreSQL, MySQL, and SQLite, with prisma studio or a direct database URL. The same page also describes embedding and VS Code.
Sources
- drizzle-orm on npm Checked
- drizzle-kit on npm Checked
- Drizzle relations Checked
- Drizzle SQLite Checked
- Drizzle with Neon Checked
- drizzle-kit studio Checked
- Drizzle Studio overview Checked
- @prisma/client on npm Checked
- prisma on npm Checked
- better-sqlite3 on npm Checked
- esbuild on npm Checked
- Upgrade to Prisma ORM 7 Checked
- Prisma ORM v7 Checked
- Prisma ORM release status Checked
- Prisma ORM v7.0.0 changelog Checked
- Engines (Prisma ORM v6) Checked
- Generators (Prisma ORM v7) Checked
- SQLite connector (Prisma ORM v7) Checked
- Database drivers (Prisma ORM v7) Checked
- PostgreSQL connector (Prisma ORM v7) Checked
- Prisma Studio Checked