Why Multitenant?
When this stack fits, host→tenant flow, and pitfalls.
Stack overview
One tenants.config.json feeds createTenantRegistry; framework packages resolve ResolvedTenant the same way in each stack.
What this stack optimizes for
- Host-based (or header-assisted) tenant resolution from a single config file shared across services.
- Markets shared across tenants (locale, currency, timezone) without duplicating boilerplate.
- Typed errors and small framework adapters so you do not fork
ResolvedTenantper app.
Host → registry → tenant
Resolution is not authentication. Knowing the tenant from the hostname does not prove who the user is. Use @multitenant/identity (or your IdP) for authorization on sensitive data.
Typical Next.js App Router path
Middleware resolves the tenant on the Edge and forwards stable headers; server code re-hydrates a ResolvedTenant via getTenantFromHeaders using the same registry module as middleware. See Next.js — App Router.
When not to use
- Tenant is purely from JWT / session, never from host — you may still use
TenantsConfigfor markets, but forced routing fromHostis the wrong mental model. - Thousands of dynamic tenants with no stable domain map — you will fight DNS and config size; consider a DB-backed resolver (out of scope for core).
- Edge DB / heavy Node work in middleware — keep middleware thin; DB access belongs in Node runtimes (see
@multitenant/database+ Getting started Edge note).
Common pitfalls
next devon rawlocalhostwithout matchingdomains.local— middleware may passthrough with no tenant; usenpx @multitenant/cli dev(ormultitenant devafter install) and hosts from your config, oronMissingTenant: 'throw'only when you control Host.- Trusting
X-Tenant-Idfrom the client — resolve from registry + trusted proxy headers, then validate session if needed. - Duplicating
ResolvedTenanttypes — import from@multitenant/coreonly. - Skipping session ↔ host alignment — if you use identity cookies, the session tenant must match the host-resolved tenant on mutating routes.