Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

A secure multi-tenant SaaS built with Next.js, Prisma, and PostgreSQL needs more than a tenantId filter in application code. Resolve the tenant from a verified hostname or session, authorize the user’s membership, pass that context through every server-side operation, and have PostgreSQL enforce row-level security (RLS) for tenant-owned rows. For most products, a shared database and shared schema with RLS is the practical starting point; schema-per-tenant or database-per-tenant becomes appropriate when contractual isolation, customization, or blast-radius requirements justify the added operational work.

What architecture should a multi-tenant Next.js SaaS use?

Use one Next.js application with a request-bound tenant context. The request should move through a predictable sequence: tenant resolution, authentication, authorization, server component or route-handler execution, tenant-aware Prisma access, and PostgreSQL RLS.

  1. Resolve the tenant: map a verified hostname, custom domain, or trusted token to a tenant record.
  2. Authenticate the principal: establish the user or service identity.
  3. Authorize membership: confirm that the principal belongs to the resolved tenant and has the required role.
  4. Create tenant context: carry the tenant identifier into server components, route handlers, jobs, and transactions.
  5. Enforce isolation in PostgreSQL: use RLS so a missed application predicate does not expose another tenant’s rows.

This is the shape recommended by the Next.js documentation for serving multiple tenants from one application. Keep resolution at the request boundary rather than rediscovering the tenant in every component. A server component should receive an already-authorized context, not infer ownership from a browser-supplied field.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why application filters alone are not enough

A query such as where: { tenantId } is useful but fragile. A future endpoint, relation traversal, export job, or delete operation can omit the predicate. PostgreSQL RLS supplies a second, database-enforced boundary. AWS Prescriptive Guidance states that “Row-level security (RLS) is required to maintain tenant data isolation in a pooled model with PostgreSQL.” RLS is a defense in depth measure; it does not replace identity, role, or business authorization checks.

#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter | 4Pack Type C Female to A Male Cable Converter for Apple Duo 17 16 15 14 Plus,iWatch Watch 12 11 10,iPad 9 8 Air,Airpods 5 4
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Which tenancy model is right: shared schema, schema-per-tenant, or database-per-tenant?

There is no universal best model. Select the boundary that matches your contractual isolation, customization, operational capacity, and workload. A shared schema is usually the simplest launch architecture, while separate schemas or databases trade higher isolation for more migrations and operations.

Model Isolation boundary Migration and operations Cost and connection impact When it fits
Shared database, shared schema, tenantId plus RLS Rows, enforced by policies One migration set and one operational surface; every tenant-owned table and policy must be correct Lowest infrastructure overhead; pooled connections are shared, so noisy-neighbor controls matter Most products with similar data shapes and no contractual requirement for a separate database
Schema-per-tenant PostgreSQL schemas Tenant-aware migration orchestration, schema discovery, connection/search-path handling, and more complex observability More catalog and connection-management work as tenants grow Customers need logical separation or tenant-specific extensions while remaining on one cluster
Database-per-tenant Separate database and credentials Provisioning, migrations, backups, monitoring, and incident response fan out for every tenant Highest infrastructure and connection overhead; strongest blast-radius boundary Regulated or contractual isolation, dedicated restore requirements, or materially different workloads

Prisma documents using multiple database clients and contracts in one Next.js application, a pattern that can support separated tenant databases. That capability does not remove the operational cost: each database still needs compatible migrations, credentials, backups, health checks, and upgrades.

How to choose without guessing at scale

  • Start with the isolation your contracts and threat model require, not a hoped-for tenant count.
  • Estimate migration fan-out, backup/restore procedures, connection limits, and on-call complexity before selecting separate databases.
  • Measure your own workload. No authoritative source establishes a universal tenant limit, performance number, or cost threshold for these models.
  • Design a migration path. A shared-schema product can later move selected customers to dedicated databases, but only if tenant identifiers, exports, and data ownership are explicit from the beginning.

How should you model tenants and memberships in Prisma?

Represent the tenant as a first-class entity and put a non-null tenant discriminator on every tenant-owned table. Keep membership separate from the user record so one person can belong to multiple organizations with different roles.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
model Tenant {
  id           String        @id @default(cuid())
  slug         String        @unique
  name         String
  status       TenantStatus  @default(ACTIVE)
  memberships  Membership[]
  projects     Project[]
  createdAt    DateTime      @default(now())
}

enum TenantStatus {
  ACTIVE
  SUSPENDED
}

model Membership {
  tenantId  String
  userId    String
  role      MembershipRole
  tenant    Tenant @relation(fields: [tenantId], references: [id])
  user      User   @relation(fields: [userId], references: [id])
  @@id([tenantId, userId])
  @@index([userId])
}

enum MembershipRole {
  OWNER
  ADMIN
  MEMBER
}

model Project {
  id        String   @id @default(cuid())
  tenantId  String
  name      String
  tenant    Tenant   @relation(fields: [tenantId], references: [id])
  createdAt DateTime @default(now())
  @@unique([tenantId, name])
  @@index([tenantId])
}

The composite uniqueness rule allows two tenants to use the same project name while preventing duplicates inside one tenant. Apply the same principle to invitations, API keys, external IDs, and any other value whose uniqueness is tenant-scoped. A nullable tenant key on a tenant-owned table creates ambiguity and should be avoided; keep platform-wide records in explicitly separate tables.

What belongs in the tenant context?

At minimum, carry the tenant ID, authenticated subject ID, membership role, and a correlation ID for logging. Treat the context as server-only data. Do not serialize a privileged role or an unverified tenant ID into a hidden form field and assume it is authoritative.

How do you resolve a tenant from a subdomain or custom domain?

Resolve the hostname at the edge or request boundary, then verify the mapping in your database before loading tenant data. The hostname is a lookup signal, not proof that the current user is authorized for that tenant.

  1. Normalize the host: remove the port, apply lowercase handling, and account for your canonical domain and local-development hostnames.
  2. Look up a configured domain record: map the host to a tenant ID using a server-side table. Handle unknown, suspended, and misconfigured domains explicitly.
  3. Authenticate: read the session or token on the server.
  4. Check membership: require an active membership for the resolved tenant before rendering a page or executing a mutation.
  5. Pass the context inward: server components and route handlers should receive the resolved tenant context rather than parse the host again.

Next.js’s multi-tenant guide demonstrates a single application serving multiple tenants with request-time routing. The exact proxy or middleware APIs vary by Next.js release, so verify the current framework configuration before copying a code sample. Keep redirects and rewrites deterministic: an unknown host should not fall through to a default customer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter | USB C Dongle with 90W Max Power Delivery, 3×5Gbps USB A Data Ports, for MacBook, Dell, and More (Charger Not Included)
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

What if a user switches organizations?

Make the active tenant an explicit server-validated choice. When a user selects another organization, verify membership, issue or update the session’s active-tenant claim, and re-run authorization on the next request. Never let a client change an organization ID and continue using the previous authorization decision.

How do you implement Prisma access without losing tenant context?

Centralize database access behind a small server-only module. Its API should require a tenant context for tenant-owned operations, making an accidental context-free query difficult to write.

type TenantContext = {
  tenantId: string;
  userId: string;
  role: 'OWNER' | 'ADMIN' | 'MEMBER';
};

export async function withTenant<T>(
  ctx: TenantContext,
  work: (tx: Prisma.TransactionClient) => Promise<T>
): Promise<T> {
  return prisma.$transaction(async (tx) => {
    // Set the PostgreSQL tenant setting for this transaction.
    await tx.$executeRaw`SELECT set_config('app.tenant_id', ${ctx.tenantId}, true)`;
    return work(tx);
  });
}

The example illustrates the control point, not a drop-in production library. The transaction-local setting must be established before every tenant query, and all work that depends on it must remain inside that transaction. Parameterize values; never concatenate a tenant ID into SQL. If you use a connection pooler, verify how transaction pooling interacts with session settings and ensure a setting cannot leak between requests.

How should ordinary Prisma queries be written?

Keep an explicit tenant predicate even when RLS is enabled. It documents intent, narrows query work, and protects environments where a table is not yet covered by a policy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const projects = await tx.project.findMany({
  where: { tenantId: ctx.tenantId },
  orderBy: { createdAt: 'desc' }
});

For nested writes, set the tenant key from the server context rather than accepting it from the request body. For relation filters, include tenant ownership on both the parent and child path where the schema permits it.

How do PostgreSQL RLS policies work with Prisma?

Enable RLS on each tenant-owned table and write policies that compare the row’s tenant key with a trusted PostgreSQL setting or role attribute. The application role used for normal traffic must not have a privilege that bypasses RLS.

ALTER TABLE "Project" ENABLE ROW LEVEL SECURITY;
ALTER TABLE "Project" FORCE ROW LEVEL SECURITY;

CREATE POLICY project_tenant_isolation
  ON "Project"
  USING ("tenantId" = current_setting('app.tenant_id', true))
  WITH CHECK ("tenantId" = current_setting('app.tenant_id', true));

USING limits rows visible to reads, updates, and deletes. WITH CHECK limits rows introduced or changed by inserts and updates. Repeat the policy pattern for every tenant-owned table, including tables reached through background jobs or administrative APIs. Decide how a missing setting behaves; a null or unset context should deny access rather than act as a wildcard.

Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter | 85W Max PD, 2 USB 3.0 & 1 USBC Data Ports, SD/TF Card Reader, for Laptops/Mac and Other Type C Devices (Charger Not Included)
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Why is the Prisma RLS example not proof of production safety?

The commonly referenced Prisma RLS example is instructional, and its README states that the extension is not intended for production use. Production code must test transaction boundaries, pooled connections, privileged roles, migrations, raw SQL, relation queries, and every bypass path. Keep migration and break-glass credentials separate from application credentials, and audit who can assume them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What is the safest implementation sequence?

Build the isolation boundary before adding product features. The following order reduces the chance that a later endpoint becomes an unscoped exception.

  1. Scaffold the Next.js application: choose the App Router and a server-component-first approach. Confirm the Node.js, Next.js, Prisma, and hosting versions required by the current documentation.
  2. Define the Prisma schema: add Tenant, Membership, and a non-null tenantId to every tenant-owned model. Add tenant-scoped composite constraints and indexes.
  3. Generate and centralize Prisma Client: use one server-side access module and prevent browser bundles from importing the client.
  4. Implement tenant resolution: map verified hosts or trusted claims to tenants; reject unknown and suspended tenants.
  5. Authorize membership and role: perform this check before constructing a data query or mutation.
  6. Set PostgreSQL context: establish the tenant setting inside a transaction and enforce RLS with an application role that cannot bypass it.
  7. Create migrations: deploy schema changes, indexes, RLS enablement, and policies in a controlled order. Seed only non-sensitive development data.
  8. Test denial cases: attempt cross-tenant reads, updates, deletes, relation traversals, exports, and background work.
  9. Deploy with production configuration: set database URLs, session secrets, domain mappings, and runtime settings through the hosting environment, not source control.

How do you test that one tenant cannot see another?

Isolation tests should try to break the boundary, not merely confirm that normal pages load. Create two tenants with similar records and exercise every access path as each user.

  • Read a record by ID belonging to the other tenant.
  • Update or delete another tenant’s record while supplying its ID in the request.
  • Insert a row with a forged tenantId.
  • Traverse relations, aggregate counts, search endpoints, and unique-key lookups.
  • Run exports, reports, webhooks, and scheduled jobs with an incorrect or missing context.
  • Open concurrent transactions and verify that a setting from one request cannot affect another.
  • Try suspended tenants, unknown hosts, expired memberships, and users removed during an active session.

Run these tests against a real PostgreSQL instance with RLS enabled. SQLite or a mocked Prisma client cannot demonstrate PostgreSQL policy behavior, connection-pool interactions, or role privileges.

How should background jobs and webhooks carry tenant identity?

Persist an explicit tenant ID with every queued job, event, and webhook delivery. A worker should load the job, verify that the tenant still exists and is active, then create a fresh tenant context for the transaction. Do not rely on the web request’s connection, process memory, or a global “current tenant” variable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For inbound webhooks, authenticate the sender, resolve the destination tenant from a server-side endpoint or credential mapping, and only then set the database context. Log the tenant identifier and job ID, but avoid writing customer payloads or secrets to ordinary logs.

What should production deployment and operations include?

A deployment is safe only when credentials, connection behavior, observability, and recovery procedures match the tenancy model.

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray | Data transfer & Fast charging for MacBook, iPhone 17/16/15, iPad, Samsung Galaxy, Apple Watch
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
  • Credentials: use separate principals for application traffic, migrations, and administration. The application principal must not bypass RLS.
  • Pooling: keep serverless concurrency and pool size within PostgreSQL’s connection limits. Validate transaction-local settings with the actual pooler mode.
  • Indexes: index tenantId and common composite access paths. Review query plans for large tenants so isolation does not hide an inefficient scan.
  • Observability: include tenant ID, request ID, route, role, and latency in structured logs and traces without recording customer data.
  • Noisy neighbors: watch latency and resource consumption by tenant. Add rate limits, job quotas, or workload separation when one customer affects others.
  • Backups and restores: document full-cluster recovery and tenant-level export, deletion, and restore procedures. A shared database backup may restore every tenant, so test how to extract one safely.
  • Preview environments: block production credentials and scrub copied data. A preview deployment should never be able to query live tenant tables.
  • Domain lifecycle: handle domain verification, reassignment, suspension, TLS, and canonical redirects as operational workflows, not just routing code.

Recheck the current Next.js, Prisma, Node.js, and hosting prerequisites before publishing commands. Framework and runtime labels change, and a configuration valid for one release may not be valid for another.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What are the most common isolation failures?

Trusting a form field for tenant identity

A hidden input such as tenantId is user-controlled. Derive the tenant from the verified request context and compare any supplied value to it only as a consistency check.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Protecting reads but not writes

A read policy without WITH CHECK can allow inserts or updates into another tenant. Test create, update, and upsert operations, not just findMany.

Leaving one table or raw query outside RLS

Inventory tenant-owned tables and raw SQL. Add migrations that enable and force RLS, then fail CI when a new tenant-owned model lacks a policy review.

Using an administrative connection for normal requests

Superusers and roles with RLS bypass privileges can make a perfect policy ineffective. Reserve them for migrations and audited break-glass work.

Leaking context through pooling

A session setting that survives longer than the intended transaction can expose data to the next request. Use transaction-local settings, test the real pooler, and inspect concurrent requests.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Assuming a successful login proves tenant access

Authentication identifies a user; membership authorizes access to a particular tenant. Perform both checks on every tenant-bound request.

Best Value
Sale
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display | 85W Max PD, 1 Powered USB-C 5Gbps & 2 Powered USB-A 3.0 5Gbps Data Ports, for MacBook Pro, MacBook Air, Dell and More (Black)
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Is a shared-schema architecture safe for a first release?

Yes, when every tenant-owned row has a non-null discriminator, tenant-scoped constraints are correct, tenant resolution is authorized, and PostgreSQL RLS is enabled and tested. It is not safe merely because each controller usually adds a tenantId filter. Treat the database policy, application role, transaction handling, and denial tests as one system.

How should you plan a later move to dedicated databases?

Keep tenant data addressable and exportable from day one. A migration can copy one tenant’s rows, recreate constraints and policies in a new database, dual-write or pause traffic during cutover, verify counts and checksums, then update the tenant-to-database routing record. The exact cutover method depends on write volume and downtime tolerance; do not assume that Prisma’s ability to create multiple clients automatically supplies data migration, consistency, or rollback.

Frequently Asked Questions

Can one user belong to more than one tenant?

Yes. Store membership as a separate join entity keyed by user and tenant, with a role on that membership. An active-tenant choice should be validated against the user’s memberships whenever it changes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I carry tenant context into a queue worker?

Persist the tenant ID in the job payload or job table, then re-check that tenant and its status when the worker starts. Create a new transaction-local PostgreSQL context for the job instead of reusing request state.

Does PostgreSQL RLS replace application authorization?

No. RLS limits which rows a database role can access, while application authorization decides whether a user may perform an operation such as billing, export, or administration. Use both layers.

Can I test RLS with SQLite during local development?

SQLite cannot reproduce PostgreSQL RLS, roles, or pooling behavior. Use a disposable PostgreSQL instance for isolation integration tests, even if a faster database is used for unrelated unit tests.

How do I restore only one customer from a shared backup?

Define a tenant-scoped export and restore procedure rather than assuming a full database restore can select one customer. Validate the procedure with production-like backups and ensure restored data is written under the correct tenant policies.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.