Skip to content

@nestarc/tenancy

Multi-tenancy module for NestJS with PostgreSQL Row Level Security (RLS) and Prisma support.

One line of code. Automatic tenant isolation.

TIP

Full API details are in the sub-pages.

Features

  • RLS-based isolation — PostgreSQL enforces tenant boundaries at the database level
  • AsyncLocalStorage — Zero-overhead request-scoped tenant context (no REQUEST scope)
  • Prisma Client Extensions — Automatic set_config() before every query
  • 5 built-in extractors — Header, Subdomain, JWT Claim, Path, Composite (fallback chain)
  • Lifecycle hooksonTenantResolved / onTenantNotFound for logging, auditing, custom error handling
  • Auto-inject tenant ID — Optionally inject tenant_id into create / createMany / upsert operations
  • Shared models — Whitelist models that skip RLS (e.g., Country, Currency)
  • withoutTenant() — programmatic bypass for background jobs and admin queries
  • tenancyTransaction() — interactive transaction support with RLS
  • Fail-Closed modefailClosed: true blocks model queries without tenant context, preventing accidental data exposure
  • Testing utilitiesTestTenancyModule, withTenant(), expectTenantIsolation() via @nestarc/tenancy/testing
  • Event system — optional @nestjs/event-emitter integration for tenant.resolved, tenant.not_found, etc.
  • Microservice propagation — HTTP (propagateTenantHeaders()), Bull, Kafka, gRPC propagators with zero transport dependencies
  • Inbound context restorationTenantContextInterceptor auto-restores tenant context from incoming microservice messages
  • Tenant-aware cachingTenantCacheInterceptor scopes Nest response cache keys by tenant, with explicit shared-cache opt-in
  • Error hierarchyTenantContextMissingError base class enables unified instanceof catch handling
  • CLI scaffoldingnpx @nestarc/tenancy init generates RLS policies and module config
  • CLI drift detectionnpx @nestarc/tenancy check validates SQL against Prisma schema
  • Multi-schema support@@schema() directives generate schema-qualified SQL (e.g., "auth"."users")
  • ccTLD-aware subdomain extraction — accurate parsing for .co.uk, .co.jp, .com.au, etc.
  • SQL injection safeset_config() with bind parameters, plus UUID validation by default
  • NestJS 10 & 11 compatible, Prisma 5 & 6 compatible (CI-tested with Prisma 6; Prisma 5 unit-tested)

Performance

Measured with PostgreSQL 16.13, Prisma Client 6.19.2, 1005 rows, 500 measured iterations on Apple M1 Pro:

ScenarioRowsAvgP50P95P99
Admin direct findMany (all rows, no RLS)10053.983ms3.369ms5.444ms6.992ms
Admin tenant-filtered findMany (WHERE tenant_id, no RLS)4022.747ms2.736ms3.612ms4.686ms
app_user manual RLS transaction (set_config + findMany)4022.846ms2.614ms4.154ms5.177ms
app_user tenancy extension findMany4022.961ms2.766ms4.281ms4.800ms
app_user tenancy extension findFirst11.217ms1.192ms1.522ms1.777ms

The headline overhead is extension findMany compared with the manual RLS transaction: +0.115ms avg (+4.0%), +0.127ms p95.

Reproduce: docker compose up -d --wait && npm run bench

Prerequisites

  • Node.js >= 18
  • NestJS 10 or 11
  • Prisma 5 or 6
  • PostgreSQL (with RLS support)

Released under the MIT License.