Back to insights
Engineering

Building a multi-tenant SaaS platform: architecture decisions from AcademicGrid

Heaplex Engineering
March 15, 2025
12 min read

Building a multi-tenant SaaS platform: architecture decisions from AcademicGrid

When we started building AcademicGrid, we had one critical requirement: a single codebase that could serve K-12 schools, colleges, and universities across multiple countries, with complete data isolation and no feature differences between tenant types.

Multi-tenancy sounds simple in theory. In practice, it's a series of intentional choices that compound over time.

The multi-tenancy decision: database-per-tenant vs. row-level isolation

We considered two main approaches:

**Option 1: Separate database per tenant.** Each school gets its own PostgreSQL database. Simple isolation guarantee, but operational nightmare—migrations become multiplied complexity, backup strategy explodes, and per-tenant scaling becomes its own headache.

**Option 2: Shared database with row-level isolation.** One PostgreSQL database, but every table includes a `tenant_id` column. Data isolation is enforced in application code and database constraints.

We chose **Option 2**—shared database with strong isolation—because it gave us the reliability of a single source of truth while keeping operational overhead manageable. The trade-off: we had to be disciplined about isolation at every layer.

How we enforced isolation

At the database level, every query is scoped to the current tenant. Never a query without a tenant filter. This became a rule, enforced through code review.

At the application layer, we created a `TenantContext` that gets passed through every request. Before any database operation, we verify the current user belongs to the requested tenant.

PostgreSQL schema design for multi-tenancy

Our core tables all follow the same pattern: `school_id` (or `institution_id`) as the first column after `id`. This isn't just a foreign key—it's the tenant identifier.

We added explicit constraints at the database level to ensure data stays partitioned.

Why NestJS over Express

We chose **NestJS** (not bare Express) specifically because multi-tenant isolation requires consistent middleware and request handling.

NestJS forced us to be explicit about tenant context injection. Every service that touches the database receives the tenant context through dependency injection—no implicit global state, no accidental cross-tenant queries.

Handling background jobs with BullMQ

AcademicGrid has async work: sending attendance alerts to parents, generating end-of-semester reports, bulk importing student data. In a multi-tenant system, these jobs also need tenant awareness.

We use **BullMQ** with Redis as the queue backend. Every job includes the tenant ID as metadata.

BullMQ handles the complexity of retries, dead-letter queues, and job prioritization. We handle tenant isolation. The separation of concerns makes both easier.

Data residency: India and UAE on the same schema

AcademicGrid launched in India and expanded to the UAE. Both regions have different compliance requirements.

We solved this with a `region` field on the school record. At the infrastructure level, India schools write to an AWS RDS instance in ap-south-1 (Mumbai), while UAE schools write to an AWS RDS instance in eu-west-1.

What we'd do differently

In hindsight, a few things:

1. **RLS (Row-Level Security) earlier.** PostgreSQL's RLS feature could have pushed more isolation down to the database level. 2. **Audit logging from day one.** Every tenant mutation should be logged immediately. 3. **Tenant-aware migrations.** A migration tool that understands multi-tenancy would have saved hours.

Lessons for your multi-tenant system

If you're building multi-tenant SaaS:

  • **Tenant isolation is not optional.** Build it into your schema from day one.
  • **Make isolation explicit.** Use middleware, dependency injection, or explicit parameters.
  • **Test isolation.** Write tests that verify a user from tenant A cannot read tenant B's data.
  • **Operational tooling matters.** Build dashboards that show per-tenant metrics.

AcademicGrid now handles thousands of students across dozens of institutions on the same codebase.

---

*AcademicGrid is live at academicgrid.com. If you're building a multi-tenant SaaS and want to discuss architecture, reach out at [email protected].*

Enjoyed this?

We write about what we build. Check out more insights or get in touch to discuss your project.