← Back to archive

Repo of the Day

shauliz/node-express-multitenant

Published: Mar 25, 2023

Open repository ↗

Multitenancy package for express based applications

Summary

node-express-multitenant is a TypeScript library that adds shared-database, shared-schema multi-tenancy to Express applications built on Prisma. It pulls a tenant identifier from each request, stores it in AsyncLocalStorage, and applies it to scoped queries through a Prisma Client Extension so developers cannot accidentally skip the tenant filter.

What it is useful for

The library fits when one Express + Prisma service serves multiple customers from a single database, separating them with a column (default account_id). It applies that filter automatically across findFirst, findFirstOrThrow, findMany, findUnique/findUniqueOrThrow, create, createMany, upsert, update, updateMany, delete, deleteMany, count, aggregate, and groupBy for any model carrying the tenant column. Models without that column are left untouched.

If a request reaches the database without a tenant ID, the library throws MissingTenantContextError and fails closed rather than returning every tenant's rows. Cross-tenant work like background jobs or admin tooling can opt out per query with the documented ignoreMultitenancy: true flag.

Documented limitations to be aware of: $queryRaw and $executeRaw are not scoped, and because Prisma rejects non-unique fields in a findUnique where clause, the library rewrites findUnique and findUniqueOrThrow to findFirst and findFirstOrThrow internally. The README also recommends compound uniques that include the tenant column to avoid upsert collisions on create when the update path is correctly suppressed.

How engineers can use it

The README documents the following setup:

  1. Install with npm install node-express-multitenant. Needs Node.js >= 20, Prisma ^5 or ^6, and Express ^4 or ^5.
  2. Add an account_id column to every Prisma model that should be scoped.
  3. Mount getMultitenancyMiddleware after authentication. The default reads req.headers["account-id"], which the README flags as insecure on its own and recommends replacing with a JWT or session claim:
app.use(
  getMultitenancyMiddleware((req) => req.headers["account-id"]),
);
  1. Wrap Prisma with the extension:
const prisma = new PrismaClient().$extends(
  withMultitenancy({ tenantField: "account_id" }),
);
  1. Query normally. findMany() is filtered automatically, create calls have account_id overwritten with the request's tenant, and findUnique is rewritten to apply the tenant filter. The README points to examples/express-prisma for a full wiring sketch.

Projects still on v1 must migrate: prisma.$use and addMultitenancy are removed in favor of withMultitenancy, and the -1 placeholder has been replaced by MissingTenantContextError.