I Switched From Prisma to Drizzle on a Live SaaS. Here's What Nobody Warned Me About.

typescript dev.to

I migrated a live SaaS from Prisma to Drizzle-ORM last month. The app had 3,000 active users, 12 tables, 40 migrations in history, and a Stripe integration that touched the database on every webhook.

Zero downtime. No rollbacks. But three things surprised me that I didn't find in any migration guide.

Why I Switched

Prisma had started feeling wrong for this project. The generated client was large. Cold starts on Vercel Edge Functions were 800ms because of it. And every time I needed a raw query — a partial update, a window function, anything slightly non-standard — I was fighting the abstraction.

Drizzle is different: it's closer to SQL. The type inference is excellent. The bundle size is a tenth of Prisma's.

The Migration Path

Step 1: Run both ORMs in parallel

Don't rip out Prisma on day one. Add Drizzle alongside it and migrate table by table.

// db/prisma.ts — keep this alive
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();

// db/drizzle.ts — add this
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';

const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });
Enter fullscreen mode Exit fullscreen mode

Migrate your read-heavy, low-risk tables first. Validate the queries. Then cut over writes.

Step 2: Translate your schema by hand (don't use the generator blindly)

Drizzle has an introspection tool (drizzle-kit introspect). It works, but the output is verbose and sometimes generates incorrect nullable inference. I spent two hours debugging a bug that traced back to a generated nullable field that should have been not null.

Safer approach: write the Drizzle schema manually from your Prisma schema. It takes longer but you'll understand what you own.

// Before (Prisma schema)
model Subscription {
  id        String   @id @default(cuid())
  userId    String
  status    String
  createdAt DateTime @default(now())
}

// After (Drizzle schema)
import { pgTable, text, timestamp } from 'drizzle-orm/pg-core';

export const subscriptions = pgTable('subscriptions', {
  id:        text('id').primaryKey(),
  userId:    text('user_id').notNull(),
  status:    text('status').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Drizzle migrations don't know about Prisma's history

This is the one that bit me. Drizzle-kit generates migrations by diffing your schema against what it thinks is in the database. But it has no knowledge of migrations Prisma already ran.

Fix: run drizzle-kit introspect once to snapshot the current database state, commit that snapshot, then generate new migrations from that baseline. Never let Drizzle generate a migration that tries to CREATE TABLE on a table Prisma already created.

npx drizzle-kit introspect  # snapshot current state
# commit the generated schema
npx drizzle-kit generate    # only generates diffs from here forward
Enter fullscreen mode Exit fullscreen mode

What I Didn't Expect

Bundle size drop: The Prisma client + engine was adding ~3.2MB to my serverless bundle. Drizzle is 67KB. My Vercel Edge cold starts went from 820ms to 210ms. That alone justified the migration.

Type inference is better: Prisma's return types are wide. Drizzle's select() returns exactly what you select — no | null surprises on fields you know are set.

Drizzle migrations are plain SQL: No engine, no binary. You can read them, edit them, and run them in any Postgres client. This matters for production incidents.

What's Still Better in Prisma

Prisma's relation queries (include, select with nested objects) are genuinely more ergonomic for complex joins. Drizzle requires explicit joins. That's not worse — it's just different, and you should know SQL well enough that it doesn't slow you down.

If your team is all TypeScript developers who don't know SQL deeply, Prisma's abstractions might actually protect you.


Building a SaaS with TypeScript and need a database setup that won't blow up your cold starts? The AI SaaS Starter Kit ships with Drizzle + Supabase wired end-to-end.

More AI tools → whoffagents.com

Source: dev.to

arrow_back Back to Tutorials