Module · Branches & Multi-Tenancy
The piece that makes one codebase serve any number of schools or campuses. Branch CRUD, super-admin switcher, and the RLS audit.
The branches module is small but central. Plan ~4 hours.
If you've followed along correctly, multi-tenancy is already working — every table you built has branch_id and RLS scoped to it. This module is just the explicit UI for managing branches and a cross-branch view for super admins.
1. Branch CRUD (Super Admin Only)
/admin/branches — list, add, edit, archive.
The list is straightforward. The form has these fields:
Note: only super admins should see this menu item. Use both a UI check and an RLS policy:
The UI check hides the menu; the RLS check is the real enforcement.
2. Branch Statistics Dashboard
The super admin wants a snapshot of each branch. One RPC, one round-trip:
The super admin dashboard does one call:
And renders a table with all the numbers. Branch admins don't need this — they only see their own branch.
3. The Branch Switcher (Super Admin Only)
A super admin frequently wants to "be" a branch admin temporarily — see exactly what the Andheri admin sees. Don't fake user accounts. Instead, add a branch switcher:
Backed by a Zustand store:
Every query that lists data adds .eq('branch_id', currentBranchId) if set:
This is purely a UI filter — RLS still applies. A super admin viewing "Andheri" via the switcher would have seen all branches anyway; the switcher just adds a .eq() to filter the view.
4. The RLS Audit (Half-Day Investment, Saves Weeks Later)
Before you launch, do a deliberate RLS audit. For every table, log in as every persona and run a query. Note what you see vs what you should see.
Build a small dev-only page at /admin/dev/rls-audit that runs the audit programmatically:
Then:
- Log in as super admin → record the row count for every table.
- Log in as Andheri branch admin → record again. (Should be ≤ super counts, and zero for branches not in Andheri.)
- Log in as teacher → again. (Should only see their section's students.)
- Log in as parent → again. (Should only see their own children's data.)
Compare against your permission matrix. If anything is off, fix the RLS policy before launch. RLS bugs found after a parent saw another child's marks are not recoverable.
5. The Multi-Tenant Promise
Multi-tenancy isn't a feature — it's a promise. The promise is:
"Aurora Public School in Mumbai and St. Joseph's in Pune can both use this system, share the same codebase, share the same Supabase database, and never see each other's data."
For your training project, you'll probably only have one "school" (your fictional Aurora Public). But the architecture you've built supports any number. To add St. Joseph's as a second tenant, you would:
- Create a
schoolstable parent tobranches. - Add
school_idto every table (next tobranch_id). - Extend RLS to filter on
school_idtoo.
That's a ~half-day refactor. Worth doing if you ever plan to commercialise this. Otherwise, skip — for one school, branch-level multi-tenancy is sufficient.
6. What to Verify
- Super admin can create + edit + archive branches
- Branch admin tries to access
/admin/branches→ 403 - Branch summary dashboard shows correct numbers (cross-check against direct queries)
- Branch switcher correctly filters every list page
- RLS audit shows expected row counts for every persona
What's Next
Your school SaaS is feature-complete. Six modules — Students, Attendance, Exams, Payments, Circulars, Branches — all working, all isolated, all secure. Time to make it real: production deploy, error monitoring, edge cases, and putting it in front of someone who will actually use it.