@samveys/tenancy

Tenant resolution, scoped ORM queries, per-tenant databases, PostgreSQL row level security

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/tenancy
Import
import { currentTenant, enableRowLevelSecurity, requireTenant, runAsTenant, tenancy, tenantCache, … } from '@samveys/tenancy'

Function (9)

currentTenant Function

The tenant of the current request or job; `undefined` outside tenant context.

function currentTenant(): Tenant | undefined;

Exported from packages/tenancy/src/tenancy.ts

enableRowLevelSecurity Function

Turns on PostgreSQL row level security for `table`: rows are visible only when `tenant_id = current_setting('app.tenant_id')`. Use `withTenantSession` to set it per transaction. Superusers and table owners bypass RLS unless FORCE is set, so this also forces it.

function enableRowLevelSecurity(db: Database, table: string, column?: string): Promise<void>;

Exported from packages/tenancy/src/tenancy.ts

requireTenant Function

The current tenant, or SVS_E114 when there is none (fail closed).

function requireTenant(): Tenant;

Exported from packages/tenancy/src/tenancy.ts

runAsTenant Function

Runs `work` as `tenantId` (queue workers, scheduled tasks). The tenant is looked up again, so a job for a deleted or unknown tenant is refused (SVS_E114) instead of running unscoped.

function runAsTenant<T>(tenantId: string, lookup: TenantLookup, work: () => Promise<T> | T): Promise<T>;

Exported from packages/tenancy/src/tenancy.ts

tenancy Function

The tenancy plugin: the `tenant` middleware, `ctx.tenant`, and database-per-tenant connections.

function tenancy(options: TenancyOptions): import("@samveys/core").Plugin<TenancyOptions>;

Exported from packages/tenancy/src/tenancy.ts

tenantCache Function

The cache seen by the current tenant: every key, lock and funnel name is prefixed with the tenant id (`t:<id>:`), so tenants never read each other's entries.

function tenantCache(cache: CacheManager): CacheManager;

Exported from packages/tenancy/src/tenancy.ts

tenantDatabases Function

Database-per-tenant mode: one manager per tenant, created on first use from `connection(tenant)` and cached. `for()` returns the current tenant's database; `close()` closes every opened one.

function tenantDatabases(connection: NonNullable<TenancyOptions['database']>): {

Exported from packages/tenancy/src/tenancy.ts

tenantScoped Function

Tenant-scoped access to a model in single-database mode: every query filters on the tenant column, `create()` sets it, and a write naming another tenant is refused with SVS_E115.

function tenantScoped<M extends ModelCtor>(model: M, column?: string): {

Exported from packages/tenancy/src/tenancy.ts

withTenantSession Function

Runs `work` in a transaction where `app.tenant_id` is the current tenant (`SET LOCAL`).

function withTenantSession<T>(db: Database, work: (trx: Transaction) => Promise<T>): Promise<T>;

Exported from packages/tenancy/src/tenancy.ts

Value (4)

domain Value

`acme.example.com` → `acme.example.com` (the whole host, for tenants on their own domains).

const domain: () => TenantResolver;

Exported from packages/tenancy/src/tenancy.ts

header Value

`X-Tenant: acme` (use only behind a gateway that sets it; clients could send any value).

const header: (name?: string) => TenantResolver;

Exported from packages/tenancy/src/tenancy.ts

path Value

`/t/acme/...` with `prefix: 't'` → `acme`.

const path: (prefix?: string) => TenantResolver;

Exported from packages/tenancy/src/tenancy.ts

subdomain Value

`acme.app.example.com` with `base: 'app.example.com'` → `acme`. Anything else resolves to nothing.

const subdomain: (base: string) => TenantResolver;

Exported from packages/tenancy/src/tenancy.ts

Interface (2)

TenancyOptions Interface

No documentation comment.

interface TenancyOptions {
    /** Resolvers tried in order: `domain()`, `subdomain()`, `path()`, `header()`. */
    readonly resolvers: readonly TenantResolver[];
    readonly lookup: TenantLookup;
    /** Database per tenant: a connection for the tenant, registered lazily as `tenant:<id>`. */
    readonly database?: (tenant: Tenant) => ConnectionConfig;

Exported from packages/tenancy/src/contracts.ts

Tenant Interface

Public types of `@samveys/tenancy`. Locked once written: change with an ADR.

interface Tenant {
    readonly id: string;
    /** Optional per-tenant data (plan, settings). */
    readonly data?: Readonly<Record<string, unknown>>;
}

Exported from packages/tenancy/src/contracts.ts

Type (2)

TenantLookup Type

Looks a tenant up by identifier; `undefined` for an unknown tenant (→ 404 SVS_E114).

type TenantLookup = (identifier: string) => Promise<Tenant | undefined> | Tenant | undefined;

Exported from packages/tenancy/src/contracts.ts

TenantResolver Type

Finds the tenant identifier of a request; `undefined` when the request carries none.

type TenantResolver = (ctx: HttpContext) => string | undefined;

Exported from packages/tenancy/src/contracts.ts