Shipmunks

Drizzle vs Prisma: bundle size and migrations, measured

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.

Minified esbuild bundle of a Node handler that inserts one user and one post, then reads the user with posts, measured October 30, 2026. better-sqlite3 is external. Drizzle is 71,691 bytes. Prisma is 4,679,321 bytes. Gzip of those files is 19,466 bytes and 1,656,726 bytes.
On this page
  1. The short answer
  2. How I tested
  3. Bundle size and install footprint
  4. Drizzle vs Prisma performance
  5. Schema and drizzle orm relations
  6. Migrations: drizzle kit vs Prisma Migrate
  7. Postgres hosts
  8. Which to pick
  9. FAQ
  10. Sources
  11. Related

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.

Install and bundle bytes, October 30, 2026
MeasurementDrizzlePrisma
node_modules after npm install --omit=dev38,245,453 bytes349,330,174 bytes
node_modules after the CLI package is requested too81,954,366 bytes349,330,174 bytes, then 349,330,587 after prisma generate
Generated client on diskNone. The schema file is the client.139,403 bytes, 10 files
Minified bundle, better-sqlite3 external71,691 bytes4,679,321 bytes
Gzip of that file19,466 bytes1,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

Minified esbuild bundle of a Node handler that inserts one user and one post, then reads the user with posts, measured October 30, 2026. better-sqlite3 is external. Drizzle is 71,691 bytes. Prisma is 4,679,321 bytes. Gzip of those files is 19,466 bytes and 1,656,726 bytes.
Bar length uses the Prisma file as full width. Gzip is in the caption of each row, not in the bar.

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.

Installed node_modules from du -sb on October 30, 2026. Drizzle with drizzle-orm and better-sqlite3 is 38,245,453 bytes. Drizzle plus drizzle-kit is 81,954,366 bytes. Prisma after npm install --omit=dev is 349,330,174 bytes, and that tree still contained the prisma CLI.
Bar length uses 349,330,174 bytes as full width. The Prisma bar is the --omit=dev install. prisma generate added 413 bytes after that, which is not a separate bar.

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.

Median milliseconds, eleven fresh processes, October 30, 2026
ClockDrizzlePrisma
Source file, import235.6 ms83.4 ms
Source file, insert plus relational read11.4 ms72.8 ms
Source file, process start through the read247.9 ms (240.6 to 253.0)156.2 ms (148.4 to 173.8)
Minified bundle, import24.9 ms60.5 ms
Minified bundle, insert plus relational read12.9 ms64.8 ms
Minified bundle, process start through the read38.7 ms (34.8 to 40.6)125.8 ms (124.2 to 130.5)
Median time from process start through one insert and one relational read. Eleven runs. Local SQLite.
  • Drizzle source, run.mjs 247.9 ms
  • Prisma source, run.ts 156.2 ms
  • Drizzle bundle, dist/run.mjs 38.7 ms
  • Prisma bundle, dist/run.mjs 125.8 ms

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.

Drizzle schema.mjs
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] }),
}));
Drizzle queries.mjs
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 },
  });
}
prisma/schema.prisma
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])
}
Prisma queries.mjs
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.

drizzle/0000_init.sql
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.

prisma/migrations/20261030091729_init/migration.sql
-- 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.ts ran 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 pull wrote 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.
Which ORM, from this run only
SituationPickWhy, from this run
Minified bundle and bundle cold startDrizzle 0.45.471,691 bytes and 38.7 ms median, against 4,679,321 bytes and 125.8 ms
Unbundled source cold startPrisma 7.10.1156.2 ms median against 247.9 ms. The import was 83.4 ms against 235.6 ms
Install footprintDrizzle38,245,453 bytes runtime, 81,954,366 with drizzle-kit. Prisma was 349,330,174 and still had the CLI
Migration commandEither, they differdrizzle-kit wrote 15 lines and named user_id. prisma migrate dev wrote 18 lines and named userId
Moving an existing Prisma SQLite filedrizzle-kit pull, then rewrite queriespull 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. Blog

FAQ

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

  1. drizzle-orm on npm Checked
  2. drizzle-kit on npm Checked
  3. Drizzle relations Checked
  4. Drizzle SQLite Checked
  5. Drizzle with Neon Checked
  6. drizzle-kit studio Checked
  7. Drizzle Studio overview Checked
  8. @prisma/client on npm Checked
  9. prisma on npm Checked
  10. better-sqlite3 on npm Checked
  11. esbuild on npm Checked
  12. Upgrade to Prisma ORM 7 Checked
  13. Prisma ORM v7 Checked
  14. Prisma ORM release status Checked
  15. Prisma ORM v7.0.0 changelog Checked
  16. Engines (Prisma ORM v6) Checked
  17. Generators (Prisma ORM v7) Checked
  18. SQLite connector (Prisma ORM v7) Checked
  19. Database drivers (Prisma ORM v7) Checked
  20. PostgreSQL connector (Prisma ORM v7) Checked
  21. Prisma Studio Checked