Back to Blog
Engineering7 min read

How to Architect a Multi-Tenant SaaS Application from Scratch

The Three Multi-Tenancy Models

Every SaaS faces the same fundamental question: how do you serve multiple customers from one application while keeping their data completely isolated? There are three models, each with different trade-offs.

1. Database per Tenant

Each customer gets their own database. Maximum isolation, easy to move customers between infrastructure, simple queries. But expensive to operate and operationally complex — schema migrations require updating every database. Reserved for high-compliance industries where data isolation is a hard legal requirement.

2. Schema per Tenant (PostgreSQL)

Each tenant gets their own schema within one database. Good isolation, easier migrations than database-per-tenant, still manageable at hundreds of tenants. Works well for B2B SaaS with sophisticated enterprise clients.

3. Shared Database with Row-Level Security

All tenants share tables, with a tenant_id column on every table and RLS policies enforcing isolation at the database level. Most cost-efficient, easiest to operate, and the right choice for most SaaS products. This is what I use by default.

Implementing Row-Level Security in PostgreSQL

-- Enable RLS on your table
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

-- Policy: users can only see rows belonging to their tenant
CREATE POLICY tenant_isolation ON projects
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid);

-- Set the tenant context at the start of each request
SET LOCAL app.current_tenant_id = '...tenant-uuid...';

With this in place, every query automatically filters by the current tenant. There is no way to accidentally expose another tenant's data — the database enforces the isolation, not the application code.

Tenant Resolution

Tenants are identified by subdomain (company.yourapp.com) or custom domain. Next.js middleware reads the host header, looks up the tenant in a fast cache (Redis or Vercel Edge Config), and sets the tenant context before the request reaches any route handler.

Onboarding New Tenants

New tenant creation should be atomic: create the organization record, set up the default roles and permissions, invite the first user, and trigger any initial setup jobs — all in a database transaction. If any step fails, roll back. Partial tenant setups cause subtle bugs that are painful to debug later.

Schema Migrations

Prisma handles migrations cleanly for shared-database multi-tenancy. Run prisma migrate deploy once and all tenants are updated simultaneously. For schema-per-tenant models, you need a migration runner that iterates over all schemas — manageable, but adds operational overhead.

I have shipped four multi-tenant SaaS products using this architecture. Reach out if you are planning a SaaS build — early architecture decisions save months of refactoring later.

SaaSArchitecturePostgreSQLTypeScript

Hire me for similar projects

Looking for a developer who can build what you just read about? Let's talk.

Get in Touch