Multi-Tenancy Overview
Learn how OmniBase handles organizations, team management, and data isolation
Multi-Tenancy
OmniBase provides a complete multi-tenant architecture with organization management, team invitations, role-based access control, and automatic data isolation via PostgreSQL Row-Level Security.
Key Features
Tenant Management
Create organizations, switch contexts, and manage tenant lifecycle
Team Invitations
Invite users, manage memberships, and onboard team members
RBAC
Configure roles, permissions, and fine-grained access control
Data Isolation
Row-Level Security, JWT tokens, and tenant-scoped queries
Architecture
OmniBase implements a Row-Level Security (RLS) based multi-tenancy model with three layers:
┌─────────────────────────────────────────────────────────────────┐
│ USER REQUEST │
│ (Cookie/Session Token + Active Tenant Context) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ AUTHENTICATION LAYER │
│ OmniBase Auth (Identity + Sessions) │
│ ┌─────────────────────────────────────────┐ │
│ │ JWT Claims: { user_id, tenant_id, role }│ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ AUTHORIZATION LAYER │
│ OmniBase Permissions (ReBAC) │
│ ┌─────────────────────────────────────────┐ │
│ │ Relation Tuples: │ │
│ │ (Tenant, acme-corp, owner, user:alice) │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ PostgreSQL with Row-Level Security │
│ ┌─────────────────────────────────────────┐ │
│ │ WHERE tenant_id = auth.active_tenant() │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘What is a Tenant?
A tenant represents an organization, workspace, or account that groups users together. Each tenant has:
- Isolated data via PostgreSQL Row-Level Security (RLS)
- A dedicated Stripe customer for billing
- Role-based access control (RBAC) for members
- Invitation system for onboarding users
- Custom roles with fine-grained permissions
Quick Start
Install the SDK
bun add @omnibase/core-jsCreate a Tenant
import { Configuration, V1TenantsApi } from '@omnibase/core-js';
const config = new Configuration({
basePath: process.env.OMNIBASE_API_URL,
});
const tenantsApi = new V1TenantsApi(config);
const { data } = await tenantsApi.createTenant({
createTenantRequest: {
name: 'Acme Corporation',
billingEmail: 'billing@acme.com',
},
});
console.log('Tenant ID:', data.data.tenant.id);Invite Team Members
await tenantsApi.createInvite({
createTenantUserInviteRequest: {
email: 'teammate@example.com',
role: 'member',
inviteUrl: 'https://app.example.com/auth/onboarding',
},
});Tenant Types
| Type | Description | Use Case |
|---|---|---|
organization | Multi-user tenant with team features | B2B SaaS, team workspaces |
individual | Single-user tenant | Personal accounts, solo users |
SDK Options
Best for: Server-side rendering, App Router, full-stack applications
// Server Action for tenant switching
'use server';
import { V1TenantsApi } from '@omnibase/core-js';
import { revalidatePath } from 'next/cache';
export async function switchTenant(formData: FormData) {
const tenantId = formData.get('tenant_id') as string;
const tenantsApi = new V1TenantsApi(config);
await tenantsApi.switchActiveTenant({
switchTenantRequest: { tenantId },
});
revalidatePath('/');
}Best for: Client-side applications, SPAs
import { useCallback } from 'react';
import { V1TenantsApi } from '@omnibase/core-js';
function TenantSwitcher({ tenants }) {
const switchTenant = useCallback(async (tenantId: string) => {
const api = new V1TenantsApi(config);
await api.switchActiveTenant({
switchTenantRequest: { tenantId },
});
window.location.reload();
}, []);
return (
<select onChange={(e) => switchTenant(e.target.value)}>
{tenants.map((t) => (
<option key={t.id} value={t.id}>{t.name}</option>
))}
</select>
);
}Best for: Server-to-server, custom implementations
import { Configuration, V1TenantsApi } from '@omnibase/core-js';
const config = new Configuration({
basePath: process.env.OMNIBASE_API_URL,
headers: { 'X-Service-Key': process.env.OMNIBASE_SERVICE_KEY },
});
const tenantsApi = new V1TenantsApi(config);
// Create tenant for a user
const { data } = await tenantsApi.createTenant(
{
createTenantRequest: { name: 'New Org' },
},
{ headers: { 'X-User-Id': userId } }
);Common Patterns
Personal Workspace Pattern
For apps where each user has their own isolated workspace:
- Create one tenant per user at signup
- Each user owns their own tenant with their own Stripe account
- Skip tenant switching UI (unless they create additional workspaces)
- RLS isolates each user's data automatically
Personal + Team Workspaces
Support both individual and team contexts:
// Personal workspace
const personal = await tenantsApi.createTenant({
createTenantRequest: {
name: 'My Workspace',
type: 'individual',
},
});
// Team workspace
const team = await tenantsApi.createTenant({
createTenantRequest: {
name: 'Acme Team',
type: 'organization',
},
});