Cloud

Multi-Tenancy

Tenant isolation middleware with header, subdomain, JWT, and custom resolution strategies.

2 min readDocumentationEdit this page

Overview

The multi-tenancy middleware resolves, validates, and attaches the current tenant to every incoming request. It supports four resolution strategies and automatically rejects suspended or archived tenants.

Basic Usage

typescript
import { multiTenancy, getTenant, getTenantId } from '@axiljs/cloud'
 
app.use(multiTenancy({
  strategy: 'header',
  headerName: 'x-tenant-id',
  resolveTenant: async (id) => await db.tenants.findById(id),
  required: true,
}))
 
app.get('/api/data', (req, res) => {
  const tenant = getTenant(req)
  res.json({ tenantId: tenant.id, plan: tenant.plan })
})

Resolution Strategies

StrategySourceExample
headerConfigurable request headerx-tenant-id: acme-corp
subdomainFirst segment of the Host headeracme.app.io → acme
jwtreq.locals.user.tenantId from authPopulated by @axiljs/auth
customUser-supplied async resolver functionAny logic you define

Header Strategy (Default)

typescript
app.use(multiTenancy({
  strategy: 'header',
  headerName: 'x-tenant-id',
}))

Subdomain Strategy

typescript
app.use(multiTenancy({
  strategy: 'subdomain',
  // acme.app.example.com → tenantId = 'acme'
}))

JWT Strategy

typescript
import { authenticate } from '@axiljs/auth'
 
app.use(authenticate(jwt))
app.use(multiTenancy({ strategy: 'jwt' }))
// Reads req.locals.user.tenantId automatically

Custom Strategy

typescript
app.use(multiTenancy({
  strategy: 'custom',
  customResolver: async (req) => {
    const token = req.headers['x-api-key']
    const org = await lookupOrgByApiKey(token)
    return org?.id ?? null
  },
}))

Tenant Validation

The middleware validates tenant ID format and lifecycle status automatically.

typescript
app.use(multiTenancy({
  strategy: 'header',
  validateTenantId: (id) => /^[a-z0-9-]{2,64}$/.test(id),
  resolveTenant: async (id) => {
    const tenant = await db.tenants.findById(id)
    return tenant
  },
}))
  • Malformed IDs → 401 Unauthorized
  • Suspended tenants → 401 Unauthorized
  • Archived tenants → 401 Unauthorized
  • Unknown tenants → 401 Unauthorized (when required: true)

Helper Functions

typescript
import { getTenant, getTenantId } from '@axiljs/cloud'
 
const tenant = getTenant(req)   // throws if missing
const id = getTenantId(req)     // shorthand for tenant.id

Options Reference

OptionTypeDefaultDescription
strategy'header' | 'subdomain' | 'jwt' | 'custom''header'Resolution method
headerNamestring'x-tenant-id'Header name for header strategy
resolveTenant(id: string) => Promise<Tenant | null>Identity lookupDatabase/cache lookup function
validateTenantId(id: string) => booleanRFC 1123 regexCustom format validation
requiredbooleantrueReject requests without a tenant
customResolver(req) => string | null—Required when strategy is custom

Help improve the documentation

AxilJS is open source and documentation improvements are welcome.

AxilJS DocumentationMIT License · Built by SyntaxilitY