NestJS
Scope every NestJS request with a module and interceptor - Express or Fastify, any ORM.
tenancyjs-integration-nest brings tenant scoping to NestJS 11. Register the tenancy module and an
interceptor opens a tenant scope for the lifetime of each request; your controllers and providers run
inside it. It works on both the Express and Fastify platforms.
Works with any ORM. Prisma · Knex · TypeORM · Sequelize · Drizzle · Mongoose.
NestJS is fully supported, but tenancyjs-cli init doesn't scaffold it yet - this is the manual setup (a
few lines). See Installation.
Dual ESM + CommonJS. As of 0.1.0 the packages ship both, so a default CommonJS nest new project
can require/import TenancyJS with no conversion — nothing special to configure.
Install
npm install tenancyjs-core tenancyjs-integration-nest tenancyjs-adapter-typeorm typeormWire it up
Provide your application-owned manager and resolution service. TenancyModule registers its global
guard and interceptor; do not register a second copy.
import { Module } from "@nestjs/common";
import { TenantResolutionChain, HeaderTenantResolver } from "tenancyjs-identifiers";
import { TenancyModule } from "tenancyjs-integration-nest";
import { manager, tenantStore } from "./tenancy";
const resolver = new TenantResolutionChain({
resolvers: [new HeaderTenantResolver()],
store: tenantStore,
// Resolving a tenant is not authorizing it — verify membership (or opt out
// with trustResolution). See /docs/guides/resolving-tenants.
authorize: ({ tenant, principal }) => principal.teamIds.includes(tenant.id),
});
@Module({
imports: [
TenancyModule.forRoot({
manager,
resolver,
principal: (req) => req.user,
}),
],
})
export class AppModule {}Mark tenant routes explicitly and use your adapter's protected API inside the handler:
import { Controller, Get } from "@nestjs/common";
import { TenantRoute } from "tenancyjs-integration-nest";
import { typeormTenancy } from "./tenancy";
import { Post } from "./entities/post";
@Controller("posts")
export class PostsController {
@Get()
@TenantRoute()
list() {
return typeormTenancy.run((client) =>
client.repository(Post).findBy({ status: "open" }),
);
}
}How it behaves
- Each request resolves its tenant and runs inside the scope; ORM queries through the adapter are scoped.
- A resolution failure fails closed with the correct HTTP status before your controller runs.
- Authorization guards may inject
NestTenantResolutionStoreto read the resolved tenant. The canonicalTenancyManagercontext begins later in the interceptor and covers the handler Observable. - Unmarked routes are neither tenant nor central routes.
The optional executor setting is for transaction executors whose scoped resource is already available
to application operations (for example an ALS-backed integration). Callback-only facades such as
TypeORM and Sequelize should normally be called directly in the handler as shown above; passing them as
an executor does not inject their callback client into Nest providers.
Use a different ORM
For the exact lifecycle and security boundary, see the package README.