Call Us NowRequest a Quote
Back to Blog
Web Development
August 19, 2026
15 min read

Architecting Multi-Tenant B2B SaaS: Next.js 15 App Router, Dynamic Subdomains & PostgreSQL Row-Level Security in 2026

Induji Technical Team

Induji Technical Team

Content Strategy

Architecting Multi-Tenant B2B SaaS: Next.js 15 App Router, Dynamic Subdomains & PostgreSQL Row-Level Security in 2026

Introduction: Modernizing Enterprise Multi-Tenant Architecture in 2026

B2B SaaS platforms are expected to deliver absolute tenant isolation, near-zero query overhead, and instant custom domain routing. Traditional multi-tenant strategies—which relied on completely separate database instances per customer or unisolated shared queries vulnerable to SQL leakage—create either exorbitant cloud hosting bills or severe security compliance risks.

In 2026, enterprise web architects adopt Shared-Database Row-Level Security (RLS) Multi-Tenancy. Combining Next.js 15 App Router edge middleware with PostgreSQL Row-Level Security (RLS) policies guarantees that every database query automatically filters records by the active tenant ID at the database engine kernel level.

Even if an application developer omits a WHERE tenant_id = '...' clause in application code, PostgreSQL's RLS kernel policy rejects unauthorized cross-tenant data access attempt, ensuring complete data isolation and meeting strict compliance standards.

This architectural guide covers configuring Next.js 15 edge middleware for dynamic custom domain routing, establishing tenant-scoped PostgreSQL connection pooling with Drizzle ORM / Prisma, configuring RLS security policies, and demonstrating how partnering with an enterprise web development specialist ensures secure SaaS scalability.


What is Shared-Database Row-Level Security (RLS) Multi-Tenancy?

Shared-Database RLS Multi-Tenancy is an architectural model where all tenants share a unified PostgreSQL database, but database-level security policies enforce tenant data boundaries. Each database session sets a runtime configuration parameter (app.current_tenant_id), restricting all SELECT, UPDATE, and DELETE queries to the authorized tenant context.


Technical Architecture Blueprint: Enterprise Next.js 15 Multi-Tenant Ecosystem

To explore advanced Next.js 15 rendering features and server actions, check our guide on enterprise Next.js 15 web development and headless architecture.

                      INCOMING HTTP REQUEST (CUSTOM DOMAIN / SUBDOMAIN)
                             (tenant-a.saas.com OR app.client.com)
                                        |
                                        v  (Edge Middleware Domain Resolution)
                    +---------------------------------------+
                    |    Next.js 15 App Router Middleware    |
                    |  (Resolves Host -> Tenant UUID)       |
                    +---------------------------------------+
                                        |
                                        v  (Injects X-Tenant-ID Header)
                    +---------------------------------------+
                    |     Next.js Server Actions / API      |
                    |   (Drizzle / Prisma Transaction Context) |
                    +---------------------------------------+
                                        |
                                        v  (Executes SET LOCAL app.current_tenant_id)
                    +---------------------------------------+
                    |   PgBouncer / Transaction Connection   |
                    +---------------------------------------+
                                        |
                                        v  (Kernel-Level RLS Policy Enforcement)
                    +---------------------------------------+
                    |  PostgreSQL Database Engine (RLS)     |
                    |  (100% Guaranteed Tenant Isolation)   |
                    +---------------------------------------+

Technical Implementation Code Snippets

1. Next.js 15 Dynamic Subdomain & Custom Domain Middleware (middleware.ts)

Resolving incoming hostnames to tenant contexts and rewriting requests cleanly in edge middleware.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const url = request.nextUrl;
  const hostname = request.headers.get('host') || '';

  // Extract root domain from environment (e.g., enterprise-saas.com)
  const rootDomain = process.env.NEXT_PUBLIC_ROOT_DOMAIN || 'saas.com';

  let tenantIdentifier: string | null = null;

  if (hostname.endsWith(`.${rootDomain}`)) {
    // Subdomain request (e.g. acme.saas.com -> acme)
    tenantIdentifier = hostname.replace(`.${rootDomain}`, '');
  } else if (hostname !== rootDomain && !hostname.startsWith('www.')) {
    // Custom domain request (e.g. portal.acme.com)
    tenantIdentifier = await resolveCustomDomainToTenant(hostname);
  }

  // Inject tenant identifier header for downstream Server Actions
  const response = NextResponse.next();
  if (tenantIdentifier) {
    response.headers.set('x-tenant-id', tenantIdentifier);
    // Rewrite path to tenant app route
    url.pathname = `/tenant/${tenantIdentifier}${url.pathname}`;
    return NextResponse.rewrite(url, { headers: response.headers });
  }

  return response;
}

async function resolveCustomDomainToTenant(domain: string): Promise<string> {
  // Edge Redis lookup for custom domain resolution (< 2ms latency)
  const res = await fetch(`https://edge-kv.internal/domain/${domain}`);
  const data = await res.json();
  return data.tenantId || 'default';
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

2. PostgreSQL Row-Level Security (RLS) SQL Migration (001_tenant_rls.sql)

Establishing robust database policies that strictly isolate records per tenant.

-- 001_tenant_rls.sql

-- 1. Create Enterprise Organizations Table
CREATE TABLE organizations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    subdomain VARCHAR(100) UNIQUE NOT NULL
);

-- 2. Create Tenant-Isolated Invoices Table
CREATE TABLE invoices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES organizations(id),
    amount DECIMAL(12, 2) NOT NULL,
    customer_name VARCHAR(255) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- 3. Enable Row Level Security (RLS)
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

-- 4. Create RLS Isolation Policy
CREATE POLICY tenant_isolation_policy ON invoices
    FOR ALL
    USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)
    WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid);

3. Tenant-Scoped Database Client Wrapper with Drizzle ORM (dbTenantClient.ts)

Setting the runtime configuration variable (app.current_tenant_id) within a transactional scope before query execution.

// lib/dbTenantClient.ts
import { db } from './db';
import { sql } from 'drizzle-orm';
import { headers } from 'next/headers';

export async function withTenantScope<T>(
  callback: (tx: typeof db) => Promise<T>
): Promise<T> {
  const headerList = await headers();
  const tenantId = headerList.get('x-tenant-id');

  if (!tenantId) {
    throw new Error('Unauthorized Access: Missing Tenant Context Header');
  }

  // Execute database operations within an isolated transaction boundary
  return await db.transaction(async (tx) => {
    // Set runtime session parameter for PostgreSQL RLS policy
    await tx.execute(sql`SET LOCAL app.current_tenant_id = ${tenantId}`);
    
    // Execute tenant-isolated application queries
    return await callback(tx as any);
  });
}

Enterprise Feature Matrix: Tenant Isolation Models

Architecture Metric Separate Database per Tenant Shared DB with RLS (2026 Standard)
Data Leakage Risk Zero (Physical separation) Zero (PostgreSQL Kernel RLS Enforcement)
Cloud Hosting Cost Extremely High (Scales linearly with tenants) Ultra-Low (Single pool shared resources)
Schema Migration Overhead Nightmarish (Execute N migrations across N DBs) Instant (Single migration updates all tenants)
Database Pool Efficiency Poor (Connection starvation per instance) Optimal (PgBouncer unified connection pool)
Onboarding Speed Slow (Provision new DB instance: 2–5 mins) Instant (Insert organization record: < 50ms)
Max Concurrent Tenants Hundreds of tenants maximum Tens of thousands of active tenants

Step-by-Step Deployment Roadmap for Enterprise SaaS Platforms

  1. PostgreSQL RLS Schema Architecture: Add tenant_id foreign keys to all business tables and enable ROW LEVEL SECURITY.
  2. Next.js 15 Middleware Routing: Configure edge middleware to parse custom domains and subdomains into clean header scopes.
  3. Transactional Tenant Client: Wrap ORM calls in transactional scopes that execute SET LOCAL app.current_tenant_id.
  4. PgBouncer Pooling Calibration: Configure PgBouncer in transaction mode to manage shared connection pools efficiently.
  5. Security & Penetration Testing: Audit cross-tenant query behavior with our enterprise web development specialists.

Scale Your Enterprise SaaS with Induji Technologies

At Induji Technologies, we build secure multi-tenant SaaS architectures, high-performance web applications, and scalable backend platforms. Our engineering teams help B2B SaaS companies scale securely without sacrificing performance or data isolation.

Ready to engineer a secure multi-tenant SaaS platform for your enterprise? Contact our web development team today.

Related Articles

Ready to Transform Your Business?

Partner with Induji Technologies to leverage cutting-edge solutions tailored to your unique challenges. Let's build something extraordinary together.

Architecting Multi-Tenant B2B SaaS: Next.js 15 App Router, Dynamic Subdomains & PostgreSQL Row-Level Security in 2026 | Induji Technologies Blog