Multi-Tenant SaaS with Next.js, Prisma, and Stripe in 2026
Architecture decisions for a multi-tenant SaaS in Next.js 14 — schema-per-tenant vs row-level isolation, Prisma patterns, Stripe Connect vs Stripe Billing, and the pitfalls.
What I'm building toward
I've shipped three multi-tenant SaaS apps on Next.js plus Prisma plus Stripe in the last year. The architecture decisions made on day one drive what's possible (and what's painful) for years after.
This post is the canonical pattern I now reach for, with the trade-offs that bit me on the first two before I settled on the third.
Tenant isolation: three options
The single biggest architectural choice. Three real options:
1. Row-level isolation (single database, tenantId on every row)
All tenants share one Postgres database. Every table has a tenantId column. Every query is filtered by tenantId.
Pros: Simplest operations. One DB to back up, one DB to migrate. Cheapest at scale.
Cons: A bug that forgets to filter by tenantId leaks data. You need Prisma middleware or a custom client wrapper to enforce filtering.
Use when: Most SaaS in 2026. The simplicity wins.
2. Schema-per-tenant (one DB, separate schemas)
All tenants share one Postgres instance, but each has its own schema. Queries automatically scope to the tenant's schema.
Pros: Strong isolation. Per-tenant migrations possible. Cleaner mental model.
Cons: Schema migrations across hundreds of tenants are operational pain. Prisma's schema-per-tenant story is still rough.
Use when: Healthcare, finance, anywhere you need true isolation for compliance.
3. Database-per-tenant
Each tenant gets their own Postgres instance.
Pros: Maximum isolation. Per-tenant backup or restore. Per-tenant performance tuning.
Cons: Operational nightmare. Migrations across hundreds of DBs is hard. Expensive.
Use when: Enterprise clients with regulatory requirements that demand it.
My default in 2026: row-level
For 95 percent of SaaS projects, I do row-level isolation with strict enforcement at the data layer.
The enforcement pattern:
I use Prisma's $extends API for the wrapper. About 50 lines of code, prevents an entire class of bugs.
Prisma schema design
A multi-tenant schema typically has these tables:
The User-Membership-Tenant triangle is the foundation. Users can switch between tenants without re-logging in. Their session holds the active tenantId.
Authorization: roles and permissions
Two-level model:
For most SaaS, role-based is enough. Reach for resource-level only when you need it.
Implementation: simple enum on Membership table for roles. For resource-level, a Permission table that joins User, Resource, and PermissionType.
Stripe: Billing vs Connect
Two completely different products from Stripe; I see teams confuse them.
Stripe Billing
For: charging your customers (tenants) on subscriptions. Subscription management, invoicing, payment retries, dunning emails, customer portal. This is what you want for SaaS.
Stripe Connect
For: enabling your customers to charge their own customers via your platform. Marketplaces, multi-vendor platforms. Your platform takes a fee; the rest goes to your customer.
For 95 percent of SaaS, you want Billing. Connect is for marketplace business models.
Stripe Billing integration patterns
What I do for every SaaS:
The webhook gotcha
Stripe webhooks are at-least-once delivery. You'll receive duplicates. Make sure your handlers are idempotent. Use event.id as a dedupe key — if you've processed it, skip.
Plans, limits, and feature flags
For tier-gating:
Domain handling
Each tenant gets a subdomain (acme.yourapp.com) or custom domain (app.acmecorp.com).
For subdomains: middleware extracts the subdomain, looks up the tenant, attaches to request context.
For custom domains: client adds a CNAME to their domain pointing to your app's domain. Your app uses the request's Host header to look up the tenant. Caddy or Vercel handles SSL automatically.
Onboarding flow
The flow that works:
Each step is its own page; can be paused and resumed. Don't put everything on one form.
What I regret in past projects
Two specific mistakes:
1. Built fine-grained permissions from day one
Spent two weeks building a per-resource permission system. Customers used three roles total for three years. Massive waste.
2. Used schema-per-tenant for "isolation"
Got isolation. Lost ergonomics. Every migration was a multi-step deploy across N tenants. Eventually migrated to row-level. Two months of work for negative business value.
My stack for new SaaS in 2026
Estimated build effort
For a v1 multi-tenant SaaS with auth, billing, dashboard, and one core feature: 4-6 weeks of focused engineering. Add core domain features on top.
TL;DR
If you're starting a multi-tenant SaaS and want a senior to architect the foundation correctly, contact me.
You might also like
Authentication in Next.js 14: NextAuth v5, Clerk, or Roll Your Own?
After shipping auth on 10+ Next.js projects, here's how I decide between NextAuth (Auth.js), Clerk, and custom JWT — with the gotchas each path hides.
Real-Time Apps with Next.js Server Actions and WebSockets in 2026
When Server Actions are enough, when you need a WebSocket layer, and how to wire Pusher / Soketi / Ably into a Next.js 14 App Router project without breaking SSR.
TypeScript Generics for React Engineers: A Practical Guide
The 6 generic patterns I use weekly on React + Next.js codebases — typed hooks, polymorphic components, discriminated unions, infer, constraints — without the academic noise.