Multi-tenancy
Shipfastai supports multi-tenant architectures with organizations and team management.
Architecture
We use a shared database with Row Level Security (RLS) for tenant isolation.
Users
└── Organizations (tenants)
└── Members (users in org)
└── Data (isolated per org)
Database Schema
Organizations Table
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
owner_id UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT now()
);
Members Table
CREATE TABLE organization_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES organizations(id),
user_id UUID REFERENCES auth.users(id),
role TEXT DEFAULT 'member',
UNIQUE(organization_id, user_id)
);
Row Level Security
Isolate data per organization:
-- Enable RLS
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
-- Policy: Users can only see their org's documents
CREATE POLICY "Org isolation" ON documents
USING (
organization_id IN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid()
)
);
Frontend Usage
Get Current Organization
const { organization } = useOrganization();
Switch Organizations
const { setOrganization, organizations } = useOrganization();
// Switch to different org
setOrganization(organizations[1].id);
Create Organization
const createOrg = async (name: string) => {
const { data, error } = await supabase
.from('organizations')
.insert({ name, slug: slugify(name) })
.select()
.single();
};
Invite Team Members
const inviteMember = async (email: string, role: string) => {
// 1. Send invitation email
await sendInviteEmail(email, organization.id);
// 2. Create pending invitation
await supabase.from('invitations').insert({
email,
organization_id: organization.id,
role,
});
};