@suaveplan/api-plugin-tenant - Multi-tenancy plugin for @suaveplan/api. Exposes @suaveplan/api-tenant's resolver chain (header / subdomain / domain / path / JWT / query / cookie) as a first-class definePlugin slot, wires TenantCache, installs the per-request resolving middleware, registers a typed tenantService in the ServiceRegistry, and (when a TenantStore is configured) mounts admin-gated CRUD routes. Server-tier. Composes with @suaveplan/api-plugin-auth's requireRole when present for RBAC on the management routes.
Multi-tenancy plugin for @suaveplan/api. Wraps
@suaveplan/api-tenant's resolver chain (header / subdomain / domain /
path / JWT / query / cookie) as a first-class definePlugin slot,
wires TenantCache, installs the per-request resolving middleware,
registers a typed tenantService in the host ServiceRegistry, and
(when a TenantStore is configured) mounts admin-gated CRUD routes
under a configurable prefix.
Documentation tree
- error-codes — Defines the stable error-code registry for
@suaveplan/api-plugin-tenantand the concreteTenantPluginErrorclass th…- error-codes — Defines the stable error-code registry for
@suaveplan/api-plugin-tenantand the concreteTenantPluginErrorclass th…
- error-codes — Defines the stable error-code registry for
- middleware — Provides
buildTenantPluginMiddleware, the Hono middleware factory the plugin installs onctx.appduringonInit. T…- tenant — Provides
buildTenantPluginMiddleware, the Hono middleware factory the plugin installs onctx.appduringonInit. T…
- tenant — Provides
- otel — Lazily resolves the OpenTelemetry instruments (
Counter,Histogram) the plugin emits from its production paths. The…- instruments — Lazily resolves the OpenTelemetry instruments (
Counter,Histogram) the plugin emits from its production paths. The…
- instruments — Lazily resolves the OpenTelemetry instruments (
- plugin — Exposes
createTenantPlugin, the factory that returns a typedPluginDescriptorconsumable by the@suaveplan/apior…- build-resolver — Composes the strategy specs from the plugin's options into a single
TenantResolverchain, optionally wrapped with `Te… - plugin — Exposes
createTenantPlugin, the factory that returns a typedPluginDescriptorconsumable by the@suaveplan/apior…
- build-resolver — Composes the strategy specs from the plugin's options into a single
- routes — Builds the five OpenAPI route entries
createTenantPluginmounts when aTenantStoreis configured:GET /,POST /…- routes — Builds the five OpenAPI route entries
createTenantPluginmounts when aTenantStoreis configured:GET /,POST /…
- routes — Builds the five OpenAPI route entries
- options — Defines the Zod schema validated at the
createTenantPluginboundary plus the strategy-spec discriminated-union schema… - routes — Defines the Zod request/response schemas the five management routes mount when the plugin is configured with a `TenantS…
- registry-augmentation — Side-effect module that declares the
tenantServiceslot on@suaveplan/api-plugin'sServiceRegistryinterface so c… - tenant-service — Defines the
TenantServiceinterface registered into the hostServiceRegistrybycreateTenantPluginand the factor… - store — Defines the canonical
TenantStorepersistence contract every@suaveplan/api-plugin-tenantbackend honours. The inte… - types — Defines the public option shapes accepted by
createTenantPluginand the resolved-config shape stored on the returned…- types — Defines the public option shapes accepted by
createTenantPluginand the resolved-config shape stored on the returned…
- types — Defines the public option shapes accepted by
Install
bun add @suaveplan/api-plugin-tenant
Quick start
import { defineApp } from "@suaveplan/api";
import { createAuthPlugin } from "@suaveplan/api-plugin-auth";
import { createTenantPlugin, MemoryTenantStore } from "@suaveplan/api-plugin-tenant";
const app = defineApp({
plugins: [
createAuthPlugin({ betterAuth: { /* ... */ } }),
createTenantPlugin({
strategies: [
{ type: "header", headerName: "x-tenant-id" },
{ type: "cookie", cookieName: "tenant" },
{ type: "subdomain", baseDomain: "app.example.com" },
{ type: "jwt", claimName: "tenantId" },
{ type: "path", prefix: "/t/" },
{ type: "query", paramName: "tenant" },
],
cache: { ttlMs: 60_000, negativeTtlMs: 5_000, maxEntries: 10_000 },
required: false,
store: new MemoryTenantStore(),
}),
],
});
What you get
- Seven resolver strategies composed in priority order — first
non-null wins. The chain is wrapped with
TenantCachewhen acacheconfig is supplied. - Per-request
c.set("tenant", tenant)via the innercreateTenantMiddlewarefrom@suaveplan/api-tenant. Status gating (deleted/suspended/pending) is enforced before the handler runs. tenantServiceregistry slot —resolveFrom,requireFrom,current,getById,listAll,createTenant,updateTenant,deleteTenant,getResolver.- Five CRUD routes mounted when
storeis supplied:GET /tenants,POST /tenants,GET /tenants/:id,PATCH /tenants/:id,DELETE /tenants/:id. Every body and query flows through a strict Zod schema, so the unified OpenAPI 3.1 doc matches the runtime surface. - Optional admin RBAC — when
@suaveplan/api-plugin-authis loaded, the CRUD routes are gated byrequireRole("admin"). Without it, the plugin logs a warning so operators do not silently expose admin endpoints. - OpenTelemetry instruments —
tenant_plugin.resolutions_total,tenant_plugin.resolution_duration_ms,tenant_plugin.cache_total,tenant_plugin.management_ops_total. - Seven stable
TENANT_PLUGIN.*error codes — surfaced viaTenantPluginError extends SuaveplanError.
Modules
error-codes/error-codes.mdtypes/types.mdstore/store.mdstore/memory.mdschemas/options.mdschemas/routes.mdservices/tenant-service.mdservices/registry-augmentation.mdmiddleware/tenant.mdotel/instruments.mdplugin/build-resolver.mdplugin/plugin.mdroutes/routes.md
Error codes
TENANT_PLUGIN.INVALID_INPUT, STORE_NOT_CONFIGURED,
SERVICE_NOT_AVAILABLE, ROUTE_PREFIX_COLLISION, TENANT_NOT_FOUND,
TENANT_SLUG_TAKEN, RESOLVER_BUILD_FAILED.
License
MIT