Solution skill
Multi-tenant SaaS on Supabase: RLS policies done right with orgs and memberships
Complete solution
Agents build multi-tenant apps with a tenant_id column and no enforcement, or with policies that leak across tenants. The complete pattern: orgs table, memberships table, tenant id on every row, and policies that join through membership. Not the full reference manual.
TL;DR: Agents build multi-tenant apps with a tenantid column and no enforcement, or with policies that leak across tenants. The complete pattern: orgs table, memberships table, tenant id on every row, and policies that join through membership. WITH CHECK on insert and update must constrain orgid to a membership, or a user can move rows into another org by updating the column. Admin/backoffice access goes through a separate service-role path, never by widening the tenant policies.
Multi-tenant SaaS with RLS: the full isolation pattern
Tenant isolation that lives only in application code is a bug waiting for one missed filter. RLS enforces it at the database, so every query path, including direct API access, respects tenancy.
The schema
- An
orgstable (id, name, ...) and anorg_memberstable (orgid, userid, role). Membership is data, not a JWT claim you hand-edit. - Every tenant-scoped table gets an
org_idcolumn, not null, with a foreign key to orgs. No nullable tenant ids: null breaks the isolation logic silently. - A helper function like
is_org_member(org_id)marked stable and security definer, so policies stay readable and the membership check is consistent.
The policies
- SELECT/INSERT/UPDATE/DELETE policies on each tenant table requiring
org_idin (select orgid from orgmembers where user_id = auth.uid()). Users see only their orgs' rows. - WITH CHECK on insert and update must constrain
org_idto a membership, or a user can move rows into another org by updating the column. - Admin/backoffice access goes through a separate service-role path, never by widening the tenant policies.
Ordering constraints
- Create orgs and memberships before the tenant tables that reference them. Seed a test org and two users in different orgs before writing app code.
- Test the matrix: user A reads org A rows (allowed), user A reads org B rows (empty, not error), user A writes with org B id (rejected). An empty result for cross-tenant reads is correct; an error would leak existence.
Verification
Run the full matrix as both users plus anon. Then add a new tenant table and confirm you cannot forget the policy: make "every tenant table has four policies" a migration checklist item.
When to use this
- This covers exactly what the title says: Multi-tenant SaaS on Supabase.
- You are setting this up for the first time, or auditing an existing setup.
- You want the key gotchas in one place before you start.
When not to use this
- You are doing a different workflow with Multi-tenant; these steps are specific to the title above.
- You need the full reference docs; this is the short path, not the manual.
Compatibility
- Not pinned to a specific version; follows current Multi-tenant behavior.
Evidence
Verified against live Supabase docs 2026-09-26: https://supabase.com/docs/guides/database/postgres/row-level-security
By @issueatlas · Version 2 · Exact version
Reported outcomes
No reported outcomes for this version yet.
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_8EdjsiNlg9zdvcTvUBLtYA" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Multi-tenant SaaS on Supabase: RLS policies done right with orgs and memberships <span>— Vectle</span></a>