Back to Home

Multi-Tenancy

Implement multi-tenant architecture with organizations, teams, and role-based access control.

PropelKit supports both single-user SaaS and team-based SaaS with a tenant context abstraction that makes your code work for both modes.

How It Works

Enable with features.multiTenancy: true.

Single-User Mode

When multiTenancy is false:

  • User logs in → goes directly to dashboard
  • Subscriptions tied to user account
  • No team management UI
  • getTenantContext() returns { type: 'user', id: userId, ownerId: userId }

Multi-Tenant Mode

When multiTenancy is true:

  • User logs in → must create or join an organization
  • Team invitations and member management
  • Role-based permissions (Owner / Admin / Member)
  • Subscriptions tied to organization
  • getTenantContext() returns { type: 'organization', id: orgId, ownerId: userId, role: 'owner' }

The Tenant Context API

TypeScript
import { getTenantContext } from '@/lib/tenancy';

const tenant = await getTenantContext();
// Single-user: { type: 'user', id: '...', ownerId: '...' }
// Multi-tenant: { type: 'organization', id: '...', ownerId: '...', role: 'owner' }
// Pending:      { type: 'pending', id: '...', ownerId: '...' }

This single API works everywhere — API routes, server components, middleware. Your code doesn't need to know which mode is active.

Role Checks

TypeScript
import { hasRole, isOwner, isOwnerOrAdmin } from '@/lib/tenancy';

await isOwner()                       // true if owner
await isOwnerOrAdmin()                // true if owner or admin
await hasRole('admin')                // true if admin
await hasRole(['owner', 'admin'])     // true if owner OR admin

Database Tables

When multi-tenancy is enabled, three additional tables are created:

  • organizations — id, name, slug, owner
  • organization_members — links users to orgs with roles
  • organization_invites — pending invitations with tokens and expiry