Skip to content

Multi-tenancy

Nwire partitions every store, every projection, and every actor by tenant when the app declares a tenant model. One process can serve N tenants with no bleed.

Declare on the app

ts
const app = await createApp({
  tenantModel: "per-org",     // or "per-account" | "per-workspace"
  tenantKey:   "x-tenant-id", // HTTP header that carries the tenant
  modules:     [submissions],
})

What happens

  • Every actor store op is keyed by (tenant, aggregateId).
  • Every projection store op is keyed by (tenant, projectionKey).
  • Every action's envelope.tenant is populated from the request header.
  • Handlers and reactions can read ctx.envelope.tenant and use it as-needed.

HTTP wire

httpInterface() reads the configured header (default x-tenant-id) and stamps the envelope. If the header is missing for a tenant-scoped action, the runtime returns 400.

bash
curl -X POST http://localhost:3000/submissions \
  -H 'x-tenant-id: acme' \
  -H 'Content-Type: application/json' \
  -d '{"studentId":"avi","answer":"א"}'

Queue + bus

Tenant flows through automatically. The envelope is serialized on dispatch and deserialized on receive — no manual passing.

Excluding a route

Some routes (sign-in, health, signup) run before a tenant exists. Mark the action tenant: "none" to opt out of the requirement.

See also

MIT licensed.