@samveys/tenancy
Dari packages/tenancy/README.md di repository framework.
Halaman ini belum diterjemahkan. Kamu sedang membaca versi bahasa Inggris.
Multi-tenancy that fails closed: code that needs a tenant and has none throws SVS_E114 (404); a write that names another tenant throws SVS_E115 (403).
Resolve the tenant per request
import { createApp } from '@samveys/core'
import { http } from '@samveys/http'
import { header, path, subdomain, tenancy } from '@samveys/tenancy'
const tenants = new Map([['acme', { id: 'acme' }]])
export const app = createApp({
name: 'portal',
plugins: [
http(),
tenancy({
resolvers: [subdomain('app.example.com'), path('t'), header('x-tenant')],
lookup: (id) => tenants.get(id),
}),
],
})Routes that need a tenant use the tenant middleware (use: ['tenant']) and read ctx.tenant. An unknown tenant is a 404. Use the header() resolver only behind a gateway that sets the header, because clients can send any value.
Keep tenants apart
| Layer | Helper | What it does |
|---|---|---|
| ORM, one database | tenantScoped(Model, 'tenantId') | Every query filters on the tenant column; create() sets it and refuses another tenant's id |
| Cache, locks | tenantCache(cache) | Prefixes every key, lock and funnel name with t:<id>:; flush() is refused |
| Workers | runAsTenant(id, lookup, work) | Looks the tenant up again before running a job; refuses unknown tenants and tenant switches |
| Database per tenant | tenantDatabases(connection) | One connection per tenant, created on first use |
| PostgreSQL | enableRowLevelSecurity(db, table) + withTenantSession(db, work) | Row level security on current_setting('app.tenant_id'), set per transaction with SET LOCAL |
Row level security does not apply to superusers or, without FORCE, to the table owner; enableRowLevelSecurity forces it, and the app should connect as a plain role.