Multi-Tenancy
Isolated workspaces, members, and invites - resolved per request with withTenant.
A tenant is an isolated workspace. Membership links users to tenants with a role. Requests select the active tenant via the X-TENANT-ID header.
Resolving the Tenant - withTenant
import { withTenant } from "@/plugins/Huruf-Tech/thunder-core/utils/withTenant.ts";
const { tenant, member, user, owner, getParentMember } = await withTenant(req);
const maybe = await withTenant(req, true); // silent modewithTenant resolves the auth session, requires X-TENANT-ID, then loads the tenant plus the caller's membership. It throws Response.forbidden() if the user is not a member or the tenant doesn't exist.
For non-owner members it also returns:
owner- the tenant creator.getParentMember()- a lazy lookup of the inviter's membership, used for RBAC role cascading.
Pair withTenant with createCRUD's isolationFields to scope a collection per tenant automatically. See the Posts CMS example for a complete multi-tenant CRUD walkthrough.
Tenancy Routes
Read each file for endpoints and exact behavior:
| Route file | Responsibility |
|---|---|
routes/tenants.ts | Tenants. Creating one atomically creates an owner member; deleting one removes its members. |
routes/tenantMembers.ts | Members. The create handler consumes a tenantInvite. |
routes/tenantInvites.ts | Invites. Validates the invited role against the caller's subRoles, renders the invite URL, and emails it. |
routes/tenantMemberships.ts | Lists the current user's memberships with the joined tenant document. |
The X-TENANT-ID header is allow-listed for CORS by the essentials hook, so browser clients can send it cross-origin.