Skip to content

Multi-Tenancy

VentureKit provides multi-tenancy support through @venturekit-pro/tenancy with flexible tenant resolution, data isolation, and quota enforcement.

Terminal window
npm install @venturekit-pro/tenancy@dev

VentureKit supports multiple ways to identify the current tenant:

StrategyExampleUse Case
subdomainacme.app.example.comSaaS with subdomains
custom-domainapp.acme.comWhite-label support
path/t/acme/api/tasksShared domain
headerX-Tenant-ID: acmeAPI-first apps
jwttenant_id claimToken-based
import { handler } from '@venturekit/runtime';
import { createTenantMiddleware } from '@venturekit-pro/tenancy';
export const main = handler(async (_body, ctx, logger) => {
const tenant = ctx.tenant!;
logger.info('Request for tenant', { tenantId: tenant.id, slug: tenant.slug });
return { tenantId: tenant.id };
}, {
scopes: ['api.read'],
middleware: [
createTenantMiddleware({ strategy: 'subdomain' }),
],
});

Enforce per-tenant usage limits:

import { createQuotaMiddleware, checkQuotas } from '@venturekit-pro/tenancy';
// As middleware (automatic per-request)
export const main = handler(async (_body, ctx, logger) => {
return { ok: true };
}, {
scopes: ['api.read'],
middleware: [
createTenantMiddleware({ strategy: 'subdomain' }),
createQuotaMiddleware(),
],
});
// Programmatic check
await checkQuotas(tenantId, {
apiRequests: { limit: 10000, period: 'month' },
storage: { limit: 5_000_000_000 }, // 5 GB
});

Access tenant information anywhere in your handler:

import { getCurrentTenant } from '@venturekit-pro/tenancy';
const tenant = getCurrentTenant(ctx);
// { id: 'acme', slug: 'acme', metadata: { plan: 'pro', ... } }

The tenancy package provides specific error types:

import {
TenantNotFoundError,
TenantSuspendedError,
TenantInactiveError,
QuotaExceededError,
} from '@venturekit-pro/tenancy';

These errors are automatically caught by the error boundary and returned as structured JSON responses with appropriate HTTP status codes.

For real-time WebSocket apps, pass tenantId during authentication:

{ "action": "auth", "token": "<jwt>", "tenantId": "acme" }

Then use the connectionStore for tenant-scoped messaging:

import { connectionStore } from '@venturekit/runtime';
// Broadcast within a tenant
await connectionStore.sendToTenant(domainName, stage, tenantId, data);
// Query tenant connections
const connections = await connectionStore.getByTenant(tenantId);