Skip to main content

API guide

Building Multi-Tenant APIs: Architecture Patterns 2026

Multi-tenant API architecture in 2026 — database isolation strategies, tenant-aware routing, authorization, rate limiting, and data isolation patterns.

·APIScout Team
Share:
Hero image for Building Multi-Tenant APIs: Architecture Patterns 2026

Building Multi-Tenant APIs: Architecture Patterns

A multi-tenant API must identify the tenant, authorize the requested action, and carry that context through every data and infrastructure call. The design is successful when a request cannot silently cross tenant boundaries and operators can prove the controls with tests and logs.

TL;DR verdict

For teams evaluating shared infrastructure, one option is a shared schema with PostgreSQL row security when its role, ownership, policy, and pooling constraints fit the workload. Schema-per-tenant and database-per-tenant make different isolation and operational tradeoffs. The reviewed mechanism documentation does not establish which architecture is common, so choose from threat model, failure radius, data-residency needs, restore requirements, and measured workload.

API fit matrix

Data modelIsolation seamOperational costEvaluate when
Shared database, shared schemaTenant key plus policy on each protected tableOne migration and shared capacityTenants can share infrastructure and policy behavior is testable
Shared database, separate schemasSchema selection and schema-specific privilegesMany schema objects and migration targetsNamespace separation is useful without separate database operations
Separate databasesConnection routing and database credentialsPer-database migrations, backups, pools, and monitoringFailure radius, residency, or restore boundaries justify the overhead
Hybrid placementTenant registry chooses shared or dedicated placementTwo operating pathsA defined tenant class needs a separate boundary

Keep the placement decision in a tenant registry rather than scattering it through handlers.

Resolve tenant context once

Resolve the tenant from an authenticated signal such as a scoped API key or signed token. A host or path can help choose the tenant, but it should not grant access by itself. Pass a typed tenant context to business logic instead of letting every query reconstruct it.

interface TenantContext {
  tenantId: string;
  actorId: string;
  permissions: string[];
  requestId: string;
}

async function listOrders(ctx: TenantContext) {
  requirePermission(ctx, 'orders:read');
  return withTenantTransaction(ctx.tenantId, (db) =>
    db.query('SELECT * FROM orders ORDER BY created_at DESC')
  );
}

Auth matrix

SignalUseCheck
API keyServer-to-server tenant bindingHash lookup, status, scopes, rotation, tenant ownership
JWT claimSigned tenant and actor contextIssuer, audience, signature, expiry, tenant membership
SubdomainRequest routing hintResolve to tenant, then require authenticated membership
Explicit tenant pathAdministrative resource selectionAuthorize actor for the selected tenant
Internal service identityBackground and service callsPreserve originating tenant and actor in the job payload

Run authorization after authentication and tenant resolution. Administrative roles still need an explicit tenant scope unless the operation is deliberately cross-tenant and separately audited.

PostgreSQL row security

The PostgreSQL 18 documentation explains that enabled row security policies control which rows normal queries and data-modification commands can access. If row security is enabled and no applicable policy exists, default-deny applies.

ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON orders
  USING (tenant_id = current_setting('app.current_tenant_id')::uuid)
  WITH CHECK (tenant_id = current_setting('app.current_tenant_id')::uuid);

RLS is a defense layer, not an unconditional guarantee. Superusers and roles with BYPASSRLS bypass policies, and table owners normally bypass row security unless the table is configured to force it. Separate the migration/ownership role from the restricted application role, review grants, and test with the exact role used in production.

Data-access quality table

ConcernRequired behaviorTest
Tenant propagationEvery repository method requires tenant contextType or runtime check rejects absent context
Transaction scopeContext is set inside the same transaction as protected queriesPool reuse cannot leak context
Write policyWITH CHECK rejects cross-tenant inserts and updatesAttempt to change a row's tenant key
Role behaviorApplication role cannot bypass policyRun direct SQL as the production role
Cache scopeTenant ID is part of every relevant keySame resource ID in two tenants returns distinct values
Job scopeQueued work carries immutable tenant identityWorker refuses payloads without tenant context

Use transaction-scoped context with pools

PostgreSQL exposes configuration-setting functions, including transaction-local settings. Set the tenant inside a transaction and run protected statements on that same connection.

BEGIN;
SELECT set_config('app.current_tenant_id', '00000000-0000-4000-8000-000000000123', true);
SELECT * FROM orders;
COMMIT;

The example uses a valid UUID because the policy casts the setting to uuid; in production, set the UUID for the authenticated tenant whose rows the transaction may access. The final true makes this transaction-scoped tenant context. It reduces cross-request leakage when a pooled connection is reused.

PgBouncer transaction pooling assigns a server connection for one transaction. Its feature matrix also says transaction pooling breaks session-based features and marks SET/RESET unsupported in that mode. Use transaction-local context within the transaction and do not rely on session state across transactions. Validate the exact driver and pool behavior before shipping.

Per-tenant rate-limit box

Apply limits to a stable tenant identifier after authentication. A safe policy records the unit, window, burst behavior, response, retry signal, and administrative override.

A plan table in architecture documentation is only an illustrative example; configure limits from your own product policy, service capacity, and customer contract. Keep tenant limits distinct from global dependency protection so one tenant cannot consume the reserve needed for everyone else.

Capacity and noisy-neighbor controls

Measure concurrency, query duration, lock time, queue depth, storage growth, and cache pressure by tenant. Then:

  • size pools from measured concurrency;
  • add statement or transaction budgets where the workload supports them;
  • move expensive work to tenant-scoped queues;
  • route analytical reads deliberately;
  • isolate a tenant only when its measured behavior or contract requires it; and
  • benchmark with representative policies and workload before attributing cost to RLS.

Neither PostgreSQL nor PgBouncer documentation supplies a portable connection ceiling, pool fan-in ratio, policy-overhead percentage, or sharding threshold.

Integration risk box

Failure modeConsequenceControl
Handler accepts a tenant ID from an untrusted headerCross-tenant accessBind the tenant to authenticated credentials
Application role owns protected tablesPolicy bypassSeparate owner and runtime roles; test grants
Pooler changes connection semanticsMissing or leaked contextKeep context transaction-local and integration-test the pool
Cache key omits tenant identityCross-tenant response reuseCentralize cache-key construction
Background job loses tenant contextWork runs in the wrong scopeRequire tenant identity in the queue contract
Restore process is shared but untestedRecovery cannot satisfy tenant needsTest restore granularity before promising it

Isolation tests are release gates

Create two tenants with colliding resource identifiers and exercise read, list, update, delete, cache, background-job, and direct-database paths. A useful assertion is not merely a denied HTTP response; it confirms that Tenant A cannot observe or modify Tenant B's record and that logs retain the request and tenant identities.

Test the RLS layer with the production application role. Also test bypass roles intentionally so operators know which maintenance paths sit outside the policy.

Source-backed evidence

PostgreSQL row security

The current PostgreSQL documentation supports policy behavior, default-deny, configuration settings, and the documented bypass conditions.

PgBouncer transaction pooling

The current PgBouncer feature matrix supports the transaction-assignment model and its incompatibility with session-based features. It does not supply workload sizing numbers.

Editorial limits

Placement criteria, test matrices, rate-limit design, and tenant observability are architecture guidance. Validate them against the service's threat model and measurements.

Methodology

APIScout reviewed PostgreSQL 18 documentation for row security and configuration functions plus the current PgBouncer features page on 2026-08-22. Unsupported performance thresholds, prevalence claims, compliance shortcuts, plan quotas, and third-party availability statements were removed.

Source-backed FAQ

Does RLS replace application authorization?

No. Application authorization decides whether the actor may perform the action. RLS adds a data-layer policy and reduces the impact of some query mistakes, subject to role and ownership conditions.

Can tenant context be stored in a database session?

That is unsafe to assume with transaction pooling. Keep context transaction-local, use the same transaction for protected work, and test the driver/pool combination.

When should a tenant move to a dedicated database?

When a concrete restore, residency, failure-radius, performance, or contractual requirement justifies the additional migrations, connections, backups, and monitoring.

What does the "enterprise tier" label prove?

Nothing about architecture by itself. Tie dedicated placement to an explicit requirement and an operating plan, not to a marketing label.

Sources

{/* Sources: mt-postgres-rls, mt-postgres-config, mt-pgbouncer-features. Claims: apiscout:building-multi-tenant-apis-2026:pricing_or_plan, apiscout:building-multi-tenant-apis-2026:compatibility_integrations, apiscout:building-multi-tenant-apis-2026:product_capabilities, apiscout:building-multi-tenant-apis-2026:performance_benchmarks, apiscout:building-multi-tenant-apis-2026:ranking_popularity_superlative, apiscout:building-multi-tenant-apis-2026:availability_or_provider_status. */}

The API Integration Checklist (Free PDF)

Step-by-step checklist: auth setup, rate limit handling, error codes, SDK evaluation, and pricing comparison for 50+ APIs. Used by 200+ developers.

Join 200+ developers. Unsubscribe in one click.