Skip to main content

Why I Switched from Prisma to Drizzle 2026

·PkgPulse Team
0

TL;DR

Drizzle is the right choice if you think in SQL and value control. Prisma is the right choice if you want a managed experience and are willing to pay the price. After migrating a mid-sized production app, Drizzle's bundle size and edge runtime support are genuinely better. But Prisma's migrations, relational queries, and onboarding experience are genuinely better too. The "almost switched back" moment came when writing complex relational queries in Drizzle — it's powerful but verbose. Here's the full picture.

Key Takeaways

  • Drizzle wins on: bundle size (5KB vs 40KB+), edge compatibility, SQL transparency
  • Prisma wins on: migrations, include for relations, Prisma Studio, onboarding
  • The migration: schema translation was easy; query translation took 2 days
  • Production result: Drizzle performs similarly; Drizzle debugging is harder
  • The honest recommendation: Prisma for teams, Drizzle for SQL-comfortable developers

Why I Switched

Context:
→ Mid-sized Next.js app: 15 tables, ~50 API routes, deployed on Vercel
→ Had been on Prisma for 2 years
→ Problem 1: Vercel Edge Functions
    → Prisma Client uses a query engine binary
    → Binary doesn't work in V8 isolates (Cloudflare Workers, Edge functions)
    → Had to carve out edge routes to avoid Prisma
    → Prisma Accelerate solves this but adds another managed service + cost

→ Problem 2: Bundle size
    → Prisma client: 40-100KB+ in the browser (when accidentally included)
    → Even server-side, the generated client is large
    → Drizzle: ~5KB

→ Problem 3: The "magic" bothered me
    → Prisma generates queries I can't see
    → When performance issues happened, I had to learn Prisma's IR
    → Couldn't easily add raw SQL optimizations

→ Why Drizzle:
    → "Drizzle is SQL with TypeScript types"
    → Works in edge environments
    → Tiny bundle
    → You write SQL (basically)

The Migration: What Was Easy

// Schema translation: straightforward

// Prisma schema:
model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id])
  authorId  String
  createdAt DateTime @default(now())
}

// Drizzle schema (drizzle/schema.ts):
import { pgTable, text, boolean, timestamp } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

export const users = pgTable('users', {
  id:        text('id').primaryKey().$defaultFn(() => createId()),
  email:     text('email').notNull().unique(),
  name:      text('name'),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export const posts = pgTable('posts', {
  id:        text('id').primaryKey().$defaultFn(() => createId()),
  title:     text('title').notNull(),
  content:   text('content'),
  published: boolean('published').default(false).notNull(),
  authorId:  text('author_id').notNull().references(() => users.id),
  createdAt: timestamp('created_at').defaultNow().notNull(),
});

// Relations (separate from schema):
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));

// Verdict: schema migration was mechanical. Took 3 hours for 15 tables.

The Migration: What Was Hard

// Query translation: where the 2 days went

// Prisma (simple and readable):
const users = await prisma.user.findMany({
  where: {
    posts: {
      some: {
        published: true,
        createdAt: { gte: thirtyDaysAgo },
      },
    },
  },
  include: {
    posts: {
      where: { published: true },
      orderBy: { createdAt: 'desc' },
      take: 5,
    },
    _count: { select: { posts: true } },
  },
  orderBy: { createdAt: 'desc' },
  take: 20,
});

// Drizzle (equivalent query):
const users = await db.query.users.findMany({
  where: (users, { exists, and, gte }) =>
    exists(
      db.select().from(posts).where(
        and(
          eq(posts.authorId, users.id),
          eq(posts.published, true),
          gte(posts.createdAt, thirtyDaysAgo),
        )
      )
    ),
  with: {
    posts: {
      where: eq(posts.published, true),
      orderBy: [desc(posts.createdAt)],
      limit: 5,
    },
  },
  orderBy: [desc(users.createdAt)],
  limit: 20,
});
// Note: Drizzle doesn't have _count equivalent in relational API
// Need to add a separate count query or use SQL directly

// Verdict: Drizzle's query API is more verbose for complex relational queries.
// "It's SQL" is both the feature and the drawback.
// I miss Prisma's include + _count syntax for complex aggregations.

Where Drizzle Is Genuinely Better

// 1. Edge runtime compatibility
// middleware.ts (Next.js Edge)
import { db } from './db';
import { users } from './schema';
import { eq } from 'drizzle-orm';

export async function middleware(req: NextRequest) {
  // This works in Vercel Edge, Cloudflare Workers, Deno Deploy:
  const user = await db.select().from(users)
    .where(eq(users.id, req.headers.get('x-user-id')!))
    .limit(1);

  if (!user) return NextResponse.redirect('/login');
  return NextResponse.next();
}
// Prisma: this would fail (query engine binary, no edge support)

// 2. Raw SQL when you need it (seamless):
const result = await db.execute(
  sql`
    SELECT u.id, u.name, COUNT(p.id) as post_count,
           AVG(p.views) as avg_views
    FROM users u
    LEFT JOIN posts p ON p.author_id = u.id
    WHERE u.created_at > ${thirtyDaysAgo}
    GROUP BY u.id, u.name
    HAVING COUNT(p.id) > 5
    ORDER BY avg_views DESC
    LIMIT 20
  `
);
// Prisma: raw SQL exists but feels like escape hatch
// Drizzle: raw SQL and typed queries are first-class citizens

// 3. Bundle size
// Server bundle analysis:
// Prisma client: 40-120KB (plus the binary engine)
// Drizzle + pg driver: ~15KB total

// For serverless with short cold starts: this matters

Where Prisma Is Still Better

// 1. Migrations are excellent
// prisma migrate dev --name add-user-role
// → Generates SQL migration
// → Applies it
// → Updates the Prisma Client
// → One command, fully managed

// Drizzle migrations:
// npx drizzle-kit generate
// npx drizzle-kit migrate
// → Generates SQL files
// → You manage them
// → Fine, but more manual
// → No equivalent of Prisma's migration history/rollback tooling

// 2. Prisma Studio
// npx prisma studio
// → Opens web UI to browse/edit your database
// → Incredibly useful for debugging, data exploration, seeding
// Drizzle: no equivalent (third-party tools exist but aren't integrated)

// 3. Aggregations and counts are easier
// Prisma:
const result = await prisma.post.groupBy({
  by: ['authorId'],
  _count: { id: true },
  _sum: { views: true },
  having: { id: { _count: { gt: 5 } } },
});

// Drizzle (equivalent):
const result = await db
  .select({
    authorId: posts.authorId,
    count: count(posts.id),
    totalViews: sum(posts.views),
  })
  .from(posts)
  .groupBy(posts.authorId)
  .having(gt(count(posts.id), 5));
// Both work. Prisma is just more ergonomic for this pattern.

// 4. The onboarding experience
// Prisma: schema.prisma → generate → start querying
// Drizzle: schema.ts → relations setup → query builders → more learning
// Teams new to ORMs find Prisma dramatically easier to learn

After 18 Months: The Verdict

Would I switch back?
Honestly: if I were starting today, I'd choose Drizzle for solo/small-team projects.
For a larger team or a product-focused startup: Prisma.

The real factors that determined my choice:
1. My app runs on Vercel Edge → Drizzle wins on this alone
2. I'm comfortable with SQL → Drizzle's verbosity doesn't bother me
3. Solo developer → I can live without Prisma Studio

If I had:
→ A team of 5+ developers → Prisma (easier onboarding, clearer patterns)
→ Mostly CRUD operations → Prisma (its API is better for this)
→ Complex analytics/reporting → Drizzle (raw SQL feels natural)
→ Any edge function requirement → Drizzle (clear winner)
→ Traditional server deployment → either works

The "almost switched back" moment:
Writing a report query with 4 joins, 3 aggregations, conditional filtering.
Prisma would be 10 lines. Drizzle was 35 lines.
The SQL was correct but verbose.
I kept Drizzle because of edge requirements, but I understood the tradeoff.

The meta-lesson:
ORM debates are as much about team composition and app requirements
as they are about technical merit.
Both Prisma and Drizzle are excellent.
The right answer depends on your context.

The Performance and Developer Experience Reality

The performance comparison between Prisma and Drizzle is more nuanced than the headline numbers suggest, and the developer experience comparison is almost entirely context-dependent.

On performance: Prisma's generated client initializes a WASM-based query engine on startup. In a Lambda function or Vercel Edge Function where cold starts are frequent, this initialization is a measurable overhead — cold starts with Prisma are consistently slower than with Drizzle in serverless environments. In a persistent server environment running on Kubernetes or a long-lived Node.js process, the initialization is a one-time cost that amortizes to nothing. If your deployment model is serverless, Drizzle's cold start advantage is real and worth caring about. If you're running persistent servers, this difference is largely irrelevant.

Drizzle compiles its query builder to raw SQL that you can inspect in your logs or with a single .toSQL() call. For developers who think in SQL, this transparency is genuinely useful for debugging performance issues and understanding exactly what queries are being executed. Prisma generates SQL through an internal IR, and while it's generally efficient, diagnosing unexpected query behavior requires either Prisma Studio's query inspector or enabling verbose logging — neither is as immediate as seeing the SQL directly.

The DX split is real in the other direction: Prisma Studio is a polished visual data explorer with no equivalent in the Drizzle ecosystem. For non-developer stakeholders — product managers exploring data, QA engineers seeding test records, support staff looking up user state — Prisma Studio is genuinely useful tooling that Drizzle simply doesn't have. Third-party tools like Drizzle Studio exist and are improving, but they're not at Prisma Studio's level of polish yet.

Drizzle's schema definition using pgTable() is more verbose than Prisma's .prisma schema language, but it has an important advantage: it's just TypeScript. There's no separate file format to learn, no code generation step, and no ORM-specific syntax that exists outside the language. The schema is importable, composable, and analyzable by any standard TypeScript tooling.

The 2026 Decision Framework: Prisma vs Drizzle

Rather than a verdict, what's actually useful is a decision framework — the specific signals that should push you toward one or the other.

Choose Drizzle when TypeScript-nativity is a priority and you want the ORM to be plain TypeScript with no code generation step. When you're on serverless or edge infrastructure and cold start time is a real constraint. When you or your team are SQL-fluent and want the transparency of seeing exactly what queries are being generated. When you're building a solo or small-team project and the tooling overhead of Prisma's managed workflow isn't worth it for your scale. When your workload involves complex analytics or reporting queries where dropping to raw SQL frequently is preferable to learning an abstraction's edge cases.

Choose Prisma when developer experience and onboarding quality are the top priorities — Prisma's tooling, documentation, and Studio are the best in the ORM space and meaningfully reduce the ramp-up time for new team members. When you're maintaining or extending an existing Prisma project (don't rewrite what works). When your application needs advanced features like full-text search, virtual fields, or composite types that Drizzle's feature set doesn't fully cover yet. When your team includes developers who aren't SQL-fluent and benefit from Prisma's abstraction over raw query building.

The 2026 state of both: Prisma has slowed its feature release cadence somewhat as the company focuses on sustainable profitability; Drizzle is shipping rapidly, with new dialect support, expanded SQL function coverage, and improved migration tooling arriving regularly. The ecosystem momentum is clearly with Drizzle. That said, Prisma's integrations with tRPC, Auth.js, and established Next.js patterns are wider and more battle-tested. For a new greenfield TypeScript project in 2026, Drizzle is the more natural choice — but neither option is a wrong answer, and the decision should hinge on your team's SQL comfort level and deployment model more than on any benchmark.

The Migration Friction That Nobody Writes About

Most write-ups of ORM migrations focus on the end state — the schema in its final form, the queries rewritten cleanly, the architecture improved. What they underreport is the friction period in the middle: the two or three weeks where both ORMs exist in the codebase simultaneously, the query patterns that don't map cleanly between them, and the team context-switching cost when half the queries use one API and half use another.

Schema translation is genuinely straightforward. Prisma's .prisma format and Drizzle's TypeScript schema definitions express the same information, and translating between them is largely mechanical — field types, constraints, defaults, and relations all have direct equivalents. A schema with fifteen tables takes a few hours, and the process is low-risk because schema files don't contain business logic. The generated migration SQL is the same regardless of which ORM produced the schema definition.

Query translation is where time compounds. Complex relational queries — the ones involving multiple includes, nested filtering, aggregations, and conditional ordering — require genuine rewriting, not mechanical translation. Prisma's findMany with include, where, and _count has no one-to-one equivalent in Drizzle's relational API. The Drizzle version requires explicit joins, separate count queries, and more verbose filter expressions. Each complex query needs to be understood, rewritten, tested against the same database to verify identical results, and reviewed for any subtle behavioral differences. On a production codebase with fifty-plus API routes containing non-trivial queries, this is weeks of focused engineering time, not days.

Team training is a cost that is easy to underestimate before you start. Drizzle's type system is sophisticated and its query builder patterns are not intuitive for developers coming from active-record style ORMs. The learning curve is real: developers who are comfortable writing Prisma queries fluently will spend meaningful time consulting documentation and experimenting before they reach the same fluency with Drizzle. For a team that is simultaneously shipping features during a migration, the cognitive overhead is significant.

What Drizzle Gets Right: Type Safety and SQL Transparency

Drizzle's core design philosophy — that TypeScript-native SQL is better than an ORM abstraction layer — produces genuine advantages that become clearer the more you use the library in production. The type safety story is not just about catching errors at compile time, though it does that; it is about the confidence that the type system gives you when reading code written six months ago.

In a Drizzle query, the TypeScript types flow directly from the schema definitions. The schema column types determine the query result types without any intermediate code generation step. When you change a column in the schema, TypeScript immediately surfaces every query that reads that column and needs to be checked. In Prisma, this same flow exists but passes through the generated Prisma Client — you need to run prisma generate after schema changes before TypeScript can check for type errors in queries. In practice this is a minor inconvenience, but it is one more step that can be forgotten, particularly in fast-moving branches where developers might change a schema and rerun the app without regenerating the client.

The SQL transparency benefit is most apparent during performance debugging. When a Drizzle query is slower than expected, calling .toSQL() on the query builder shows the exact SQL that will be executed. You can take that SQL, run it in your database's query analyzer, and immediately see the execution plan. There is no abstraction layer to look through. With Prisma, the generated SQL is not directly accessible from the query builder — you need to enable query logging at the Prisma Client level, which adds verbosity to all queries, or use Prisma Studio's query inspector. Neither is as immediate as Drizzle's direct access model.

The edge runtime compatibility, which was the original motivation for the migration, turns out to be a pervasive advantage rather than a narrow one. V8 isolate environments — Cloudflare Workers, Vercel Edge Functions, Deno Deploy — are increasingly where performance-sensitive logic runs, whether for geographic distribution or cold start optimization. Drizzle works in all of them with any WebSocket-capable database driver. Prisma requires either avoiding edge deployment for database-accessing routes or adopting Prisma Accelerate, which introduces a managed connection pooling proxy as an additional service dependency. For greenfield projects in 2026, not being boxed out of edge deployment from day one is a meaningful architectural freedom.

What Prisma Still Wins: Migrations Tooling and Studio

The Prisma team has invested heavily in the developer experience around schema migrations, and that investment shows in ways that become apparent immediately when you switch to a different ORM. Prisma's migration workflow — prisma migrate dev — handles the complete cycle of generating a migration file, applying it to the development database, and regenerating the Prisma Client in a single command. The migration history is stored in your repository in human-readable SQL files, and Prisma's tooling tracks which migrations have been applied in each environment, making multi-environment deployment deterministic.

Drizzle's migration tooling is functional and improving, but it requires more manual management. drizzle-kit generate produces the SQL migration files, and drizzle-kit migrate applies them, but the orchestration of tracking which migrations have run in which environments is left to the developer. For teams comfortable with SQL and migration management, this is not a significant burden. For teams that want a "it just works" migration experience, particularly when onboarding new developers or managing multiple staging environments, Prisma's managed approach removes a category of operational complexity.

Prisma Studio is the feature most commonly mentioned when developers who have migrated away from Prisma describe what they miss. It is a visual data browser and editor that opens directly against your development database, with no additional configuration — just npx prisma studio. For routine development tasks like inspecting the result of a migration, seeding test data, looking up a specific record during debugging, or verifying that an API wrote what it was supposed to write, Prisma Studio is genuinely faster than any alternative workflow. The Drizzle ecosystem has Drizzle Studio in early development and third-party tools like PgAdmin or Postico fill part of the gap, but none of them have the zero-configuration integration with the schema definition that Prisma Studio provides.

Prisma's relation querying ergonomics remain an advantage for data-heavy applications where the application layer does most of the aggregation work. The include API for loading related records is readable and concise; _count for counting related records without loading them is a genuinely useful pattern. These are table-stakes features for CRUD applications, and Prisma's implementation of them is mature and battle-tested.

The "Almost Back" Scenario: When You Miss What You Left

The moment that triggered the "almost switched back" feeling described in the verdict was writing a reporting query that combined multiple aggregations, conditional joins, and subquery filters. The Drizzle query was functionally correct and the TypeScript types were precise — but it was 40 lines of query builder code where the equivalent Prisma query would have been 12 lines. Both would produce the same SQL. The difference was entirely in the readability of the query author's intent.

This is a real trade-off and not a minor one. Code is read far more often than it is written, and a query that takes 40 lines to express what 12 lines could express is genuinely harder to maintain. Future developers modifying the query must understand not just the business logic but the Drizzle query builder patterns well enough to safely extend them. With Prisma, the same query is more approachable for developers with less Drizzle-specific experience.

The "almost back" scenario plays out in specific contexts: when the team adds developers less familiar with SQL, when reporting and analytics requirements grow beyond simple CRUD patterns, or when the verbosity of Drizzle's relational query API starts showing up in code review discussions as a readability concern. None of these would have pushed a full rollback in the case described — the edge deployment requirements alone made Prisma impractical — but for teams without that hard constraint, the verbosity question is legitimate.

The honest assessment after 18 months: Drizzle's architectural fit for the deployment model is correct, and the switch was right for the specific project. But the migration reinforced that ORM choice has real long-term DX consequences that do not fully reveal themselves until you have built a substantial feature set on the new tool and encountered its limits in production conditions.

Ecosystem Momentum and the Long-Term Bet

One dimension of the Prisma vs Drizzle decision that is easy to underweight in the short term but matters enormously over a two-to-three year horizon is ecosystem momentum. A tool's trajectory — whether it is shipping capabilities faster, attracting more contributors, growing its user community — determines whether the gap between where it is today and where you need it to be will narrow or widen over time.

Drizzle's momentum in 2026 is clearly stronger. The release cadence is faster: new SQL dialect support, expanded function coverage for each dialect, improved migration tooling, Drizzle Studio development, and expanding adapter support for edge-compatible database drivers have all shipped within the past 12 months. The GitHub contributor count is growing. The community Discord is active and responsive. For a tool that was still maturing when some early adopters committed to it, the trajectory suggests the gaps that made early Drizzle adoption risky have been or are being closed.

Prisma's momentum has slowed somewhat. The company's focus has shifted toward Prisma Accelerate and Prisma Pulse — managed cloud products — rather than the open-source Prisma Client that most developers use directly. New features for the open-source ORM arrive less frequently than they did in Prisma's rapid growth years. This is a reasonable business decision — sustainable revenue requires commercial products — but it means the gap between what Drizzle ships and what Prisma ships is widening on the feature velocity dimension.

The long-term bet implication: developers choosing Drizzle today are betting that its trajectory continues and the ecosystem matures to the point where missing features (Drizzle Studio polish, third-party integrations, documentation breadth) catch up with Prisma's current level. That is a reasonable bet given the evidence. Developers choosing Prisma today are betting that its accumulated maturity advantage — migrations tooling, Studio, documentation quality, ecosystem integrations — is durable enough that slower future velocity does not materially disadvantage them. That is also a reasonable bet, particularly for teams where stability is more valuable than access to new capabilities.

Neither bet is wrong. The bet that would be hard to justify is choosing Prisma specifically for better future development velocity — the data no longer supports that. And the bet that would be hard to justify is choosing Drizzle for a team that has not developed SQL fluency and expects the ORM to handle the complexity of relational data modeling for them — Drizzle's abstraction is intentionally thin in ways that require SQL understanding to use effectively.

Compare Prisma vs Drizzle download trends, bundle size, and health scores at PkgPulse.

See also: Mongoose vs Prisma and Knex vs Prisma, Drizzle ORM vs Prisma (2026).

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.