Forem

huangyongshan46-a11y
huangyongshan46-a11y

Posted on

Why I Switched from Supabase to Prisma + PostgreSQL for My SaaS (2026)

Why I Switched from Supabase to Prisma + PostgreSQL for My SaaS (2026)

I used Supabase for almost two years. I recommended it to people. I built real products on it.

Then I switched. Here is why.


The Supabase Appeal

Supabase is genuinely impressive for getting started. You sign up, create a project, and you have:

  • A PostgreSQL database (hosted)
  • Auth with social providers
  • Row-level security
  • Realtime subscriptions
  • Edge functions
  • Storage

All of it with a generous free tier and a clean dashboard. For a proof of concept or a solo project, the developer experience is hard to beat.

I am not here to trash Supabase. It is a good product. But after running production SaaS workloads on it, I hit friction I could not ignore.


Problem 1: Type Safety Is an Afterthought

Supabase generates TypeScript types from your schema, which sounds great. In practice, the experience is clunky.

You run a CLI command to generate types, get a massive auto-generated file, and then import from it throughout your codebase. When your schema changes, you re-run the generator and hope nothing breaks.

Compare this to Prisma. Your schema is in schema.prisma. You run prisma generate. Your Prisma Client is fully typed — queries, relations, filters, the whole thing. Your IDE knows exactly what shape every query returns. You get autocomplete on nested relations.

// Prisma: fully typed, IDE-complete
const user = await prisma.user.findUnique({
  where: { id: userId },
  include: { subscriptions: true }
});
// user.subscriptions is typed correctly — no casting needed

// Supabase: requires manual type annotation or casting
const { data: user } = await supabase
  .from('users')
  .select('*, subscriptions(*)')
  .eq('id', userId)
  .single();
// Type of user? Depends on your generated types and how you annotate
Enter fullscreen mode Exit fullscreen mode

Prisma wins on type safety. It is not close.


Problem 2: Vendor Lock-In

This one crept up on me slowly.

With Supabase, I was using:

  • Supabase Auth (with their JWT format)
  • Supabase storage
  • Supabase Edge Functions
  • Supabase Realtime
  • Row-level security policies written for their specific Postgres config

Every feature I added deepened the dependency. Supabase is open source (you can self-host), but the operational reality of self-hosting Supabase is non-trivial. It is a cluster of services.

With Prisma + raw Postgres, I can move my database to:

  • Railway
  • Neon (serverless Postgres)
  • Render
  • Fly.io
  • Any VPS with Postgres installed
  • AWS RDS

No migration of auth config, no RLS policy changes, no rewriting storage logic. Just a new DATABASE_URL.

For a small project, lock-in feels fine. For something you plan to run for years, it is a real risk.


Problem 3: Cost at Scale

Supabase free tier is generous. Supabase Pro at $25/month is reasonable. But once you grow past the included limits — database size, bandwidth, edge function invocations, storage — costs climb.

More importantly, Supabase pricing is opaque until you are already using the features. Row-level security is computationally expensive at scale. Realtime connections add up. Edge function cold starts hurt latency.

With Postgres on Railway or Neon, the pricing is predictable. You pay for compute and storage. No surprises.


Problem 4: Debugging Is Harder

When something goes wrong with a Supabase query — a slow RLS policy, a weird edge case in their JavaScript client, an auth token expiry issue — you are debugging through their abstraction layers.

With Prisma + raw Postgres, when something goes wrong:

  • You can prisma.$queryRaw to test the exact SQL
  • You can open psql and run the query manually
  • You can use EXPLAIN ANALYZE on the actual query
  • You understand exactly what is hitting the database

Owning the database layer means owning the debugging.


The Migration Was Easier Than I Expected

Switching from Supabase auth to Auth.js v5 (formerly NextAuth) took about a day. The session handling is actually cleaner with Auth.js v5 — proper TypeScript, edge-compatible, works with any database adapter.

Moving the data was a pg_dump and restore. Ten minutes.

Writing Prisma schema to match my existing Supabase tables: maybe two hours.

Total migration: about two days of focused work. Immediately felt better.


The Stack I Use Now

Next.js 16 (App Router)
Auth.js v5
Prisma ORM
PostgreSQL (Railway or Neon)
Stripe
Resend (email)
Enter fullscreen mode Exit fullscreen mode

This is also the exact stack that ships with LaunchKit — the Next.js SaaS starter kit I have been building on for new projects.

LaunchKit made this decision for me by defaulting to Prisma + Postgres instead of Supabase. Everything is set up: schema, migrations workflow, seed data, Auth.js v5 adapter. You clone it and you are on the modern stack from minute one.

No Supabase account required. No vendor account required at all beyond a Postgres host.


When Supabase Still Makes Sense

I want to be fair. There are scenarios where I would still reach for Supabase:

  • Realtime features are core to your product — Supabase Realtime is excellent and hard to replicate easily
  • You need row-level security at the database layer — Supabase RLS is genuinely powerful if you understand it
  • Fast prototype with no DevOps — Supabase removes so much setup friction for getting to MVP

But for a production SaaS where you want full control, predictable costs, and maximum type safety — Prisma + PostgreSQL is the right call.


TL;DR

  • Type safety: Prisma wins cleanly
  • Portability: Prisma + raw Postgres is database-host agnostic
  • Cost: Predictable with standard Postgres hosting
  • Debugging: Easier when you own the layer
  • Lock-in: Supabase is harder to escape than it looks

If you want to start your next SaaS on Prisma + Postgres with Auth.js v5 and Next.js 16, LaunchKit has it all wired up.

$49 on Gumroad: https://yongshan5.gumroad.com/l/xckqag

Code preview: https://github.com/huangyongshan46-a11y/launchkit-saas-preview


Still on Supabase? Thinking about switching? Drop your questions in the comments — happy to share more about the migration.

Top comments (0)