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.tenantis populated from the request header. - Handlers and reactions can read
ctx.envelope.tenantand 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
- Hash partitioning vs row-level — when to put tenants in separate databases
- @nwire/forge tenant API —
actorStore.list({ tenant }),projectionStore.byKey({ tenant, key })