122 lines
7.5 KiB
Markdown
122 lines
7.5 KiB
Markdown
# @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.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable error-code registry for `@suaveplan/api-plugin-tenant` and the concrete `TenantPluginError` class th…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable error-code registry for `@suaveplan/api-plugin-tenant` and the concrete `TenantPluginError` class th…
|
|
- [middleware](./src/middleware/tenant.md) — Provides `buildTenantPluginMiddleware`, the Hono middleware factory the plugin installs on `ctx.app` during `onInit`. T…
|
|
- [tenant](./src/middleware/tenant.md) — Provides `buildTenantPluginMiddleware`, the Hono middleware factory the plugin installs on `ctx.app` during `onInit`. T…
|
|
- [otel](./src/otel/instruments.md) — Lazily resolves the OpenTelemetry instruments (`Counter`, `Histogram`) the plugin emits from its production paths. The…
|
|
- [instruments](./src/otel/instruments.md) — Lazily resolves the OpenTelemetry instruments (`Counter`, `Histogram`) the plugin emits from its production paths. The…
|
|
- [plugin](./src/plugin/plugin.md) — Exposes `createTenantPlugin`, the factory that returns a typed `PluginDescriptor` consumable by the `@suaveplan/api` or…
|
|
- [build-resolver](./src/plugin/build-resolver.md) — Composes the strategy specs from the plugin's options into a single `TenantResolver` chain, optionally wrapped with `Te…
|
|
- [plugin](./src/plugin/plugin.md) — Exposes `createTenantPlugin`, the factory that returns a typed `PluginDescriptor` consumable by the `@suaveplan/api` or…
|
|
- [routes](./src/routes/routes.md) — Builds the five OpenAPI route entries `createTenantPlugin` mounts when a `TenantStore` is configured: `GET /`, `POST /`…
|
|
- [routes](./src/routes/routes.md) — Builds the five OpenAPI route entries `createTenantPlugin` mounts when a `TenantStore` is configured: `GET /`, `POST /`…
|
|
- [options](./src/schemas/options.md) — Defines the Zod schema validated at the `createTenantPlugin` boundary plus the strategy-spec discriminated-union schema…
|
|
- [routes](./src/schemas/routes.md) — Defines the Zod request/response schemas the five management routes mount when the plugin is configured with a `TenantS…
|
|
- [registry-augmentation](./src/services/registry-augmentation.md) — Side-effect module that declares the `tenantService` slot on `@suaveplan/api-plugin`'s `ServiceRegistry` interface so c…
|
|
- [tenant-service](./src/services/tenant-service.md) — Defines the `TenantService` interface registered into the host `ServiceRegistry` by `createTenantPlugin` and the factor…
|
|
- [store](./src/store/store.md) — Defines the canonical `TenantStore` persistence contract every `@suaveplan/api-plugin-tenant` backend honours. The inte…
|
|
- [memory](./src/store/memory.md) — Ships the default `MemoryTenantStore` — a synchronous in-memory implementation of the `TenantStore` interface suitable…
|
|
- [store](./src/store/store.md) — Defines the canonical `TenantStore` persistence contract every `@suaveplan/api-plugin-tenant` backend honours. The inte…
|
|
- [types](./src/types/types.md) — Defines the public option shapes accepted by `createTenantPlugin` and the resolved-config shape stored on the returned…
|
|
- [types](./src/types/types.md) — Defines the public option shapes accepted by `createTenantPlugin` and the resolved-config shape stored on the returned…
|
|
<!-- end:auto-docs -->
|
|
|
|
## Install
|
|
|
|
```bash
|
|
bun add @suaveplan/api-plugin-tenant
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
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 `TenantCache` when a
|
|
`cache` config is supplied.
|
|
- **Per-request `c.set("tenant", tenant)`** via the inner
|
|
`createTenantMiddleware` from `@suaveplan/api-tenant`. Status gating
|
|
(`deleted` / `suspended` / `pending`) is enforced before the handler
|
|
runs.
|
|
- **`tenantService` registry slot** — `resolveFrom`, `requireFrom`,
|
|
`current`, `getById`, `listAll`, `createTenant`, `updateTenant`,
|
|
`deleteTenant`, `getResolver`.
|
|
- **Five CRUD routes** mounted when `store` is 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-auth` is
|
|
loaded, the CRUD routes are gated by `requireRole("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 via
|
|
`TenantPluginError extends SuaveplanError`.
|
|
|
|
## Modules
|
|
|
|
- [`error-codes/error-codes.md`](./src/error-codes/error-codes.md)
|
|
- [`types/types.md`](./src/types/types.md)
|
|
- [`store/store.md`](./src/store/store.md)
|
|
- [`store/memory.md`](./src/store/memory.md)
|
|
- [`schemas/options.md`](./src/schemas/options.md)
|
|
- [`schemas/routes.md`](./src/schemas/routes.md)
|
|
- [`services/tenant-service.md`](./src/services/tenant-service.md)
|
|
- [`services/registry-augmentation.md`](./src/services/registry-augmentation.md)
|
|
- [`middleware/tenant.md`](./src/middleware/tenant.md)
|
|
- [`otel/instruments.md`](./src/otel/instruments.md)
|
|
- [`plugin/build-resolver.md`](./src/plugin/build-resolver.md)
|
|
- [`plugin/plugin.md`](./src/plugin/plugin.md)
|
|
- [`routes/routes.md`](./src/routes/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
|
|
|