One API, Many Schools: Lessons From a Multi-Tenant SaaS
On a school-management platform, every tenant shares the same code and infrastructure but must never see each other's data. Tenancy is a discipline you enforce at every layer.
NuxseedSaaS is a multi-tenant school management platform — administration, finance and payroll, media broadcasting — where every school runs on the same NestJS / PostgreSQL backend but must stay completely isolated from every other.
The temptation early on is to sprinkle `where tenant_id = ?` across your queries and call it tenancy. That works until the one time you forget — and then one school sees another's payroll. So I pushed isolation down to a place it can't be forgotten: the data-access layer and row-level policies, not the individual query.
Roles add a second axis. An admin, a teacher, and a finance officer in the same tenant see very different slices of the same data. I kept that in the API surface rather than the UI, so a dashboard can't accidentally request something the role isn't allowed to read.
The lesson: multi-tenancy isn't a feature you build once. It's an invariant you protect at every layer — schema, access, API, UI — because the cost of getting it wrong even once is trust you don't get back.