@vocoweb/tenant
v1.0.1
Published
Production-ready multi-tenancy and RBAC for B2B SaaS
Maintainers
Readme
@vocoweb/tenant
Production-ready multi-tenancy and RBAC for B2B SaaS applications.
Overview
@vocoweb/tenant is the "Tenant Guard" module that solves the nightmare of building Teams, Invites, Roles, Permissions, and Data Isolation for B2B SaaS. It provides automatic tenant_id injection, pre-built components, and complete organization management.
Key Features:
- 🏢 Organization Management - Create and manage teams/workspaces
- 👥 Role-Based Access Control - Admin, Member, Viewer roles with custom permissions
- 📧 Invite System - Email-based team invitations
- 🔒 Data Isolation - Automatic tenant_id injection in all queries
- ⚛️ Pre-built Components - Team switcher, invite UI, settings
Installation
npm install @vocoweb/tenantQuick Start
Server-Side (API Routes)
import { tenant } from '@vocoweb/tenant';
// Create organization
export async function POST(request: Request) {
const { name } = await request.json();
const user = await auth.requireUser(request);
const org = await tenant.createOrganization({
name,
ownerId: user.id,
});
return Response.json({ organization: org });
}
// Invite member
export async function POST(request: Request) {
const { email, role, organizationId } = await request.json();
await tenant.inviteMember({
organizationId,
email,
role: 'member', // or 'admin', 'viewer'
});
return Response.json({ success: true });
}Client-Side (React Components)
import { VocoTeamSwitcher, VocoInviteMember } from '@vocoweb/tenant/react';
// Navbar with team switcher
export default function Navbar() {
return (
<nav>
<VocoTeamSwitcher />
</nav>
);
}
// Settings page with invite UI
export default function TeamSettings() {
return (
<div>
<h1>Team Settings</h1>
<VocoInviteMember role="admin" />
</div>
);
}Middleware (Automatic Tenant Injection)
// middleware.ts
import { createTenantMiddleware } from '@vocoweb/tenant';
export const middleware = createTenantMiddleware({
// All database queries automatically include tenant_id
enforceIsolation: true,
});
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*'],
};API Reference
Server Methods
createOrganization(options)- Create new organizationinviteMember(options)- Send organization inviteacceptInvite(token)- Accept organization invitesetMemberRole(options)- Update member roleremoveMember(options)- Remove member from organizationgetOrganizationMembers(orgId)- List all membersgetUserOrganizations(userId)- Get user's organizations
Client Methods
switchOrganization(orgId)- Switch active organizationgetCurrentOrganization()- Get current active organization
React Components
<VocoTeamSwitcher />- Notion-style workspace switcher<VocoInviteMember role="admin" />- Invite member UI
Environment Variables
# Supabase (Required)
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# App Configuration
NEXT_PUBLIC_APP_URL=https://yourapp.com
[email protected]Database Schema
The package expects the following tables:
-- Organizations
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
owner_id UUID NOT NULL REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Organization Members
CREATE TABLE organization_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
role TEXT NOT NULL CHECK (role IN ('admin', 'member', 'viewer')),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(organization_id, user_id)
);
-- Invites
CREATE TABLE organization_invites (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
email TEXT NOT NULL,
role TEXT NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);License
MIT © VocoWeb
Support
- Email: [email protected]
- Documentation: GitHub Wiki
Built with care by VocoWeb
