$ cat ~/projects/smartcampus.mdx
SmartCampus.co.id
A shared Go and MySQL backend that runs several university environments while keeping tenants isolated.
- Next.js
- Go
- MySQL
Context
SmartCampus needed one platform for multiple universities, with each university isolated from the others. The product included auth, course delivery, content management, and payments. The team was small, so I worked across most of those areas.
What I built
- Multi-tenant backend in Go. Every request carries a tenant context (resolved from the subdomain), and every database query is scoped by
tenant_id. The schema is shared across tenants but logically partitioned via thetenant_idcolumn on every table; we considered schema-per-tenant early on and decided against it because migrations across N schemas were going to be a nightmare. - Authentication with JWT + refresh tokens, role-based access (admin, lecturer, student), and tenant-scoped permissions. The auth service is the only one that knows about password hashes; everything else trusts the JWT claims.
- Schema design for institutions, users, courses, enrollments, learning modules, content blocks, and resource metadata. Every foreign key has a
tenant_idcolumn to make "show me everything for this university" a single index lookup. - Next.js frontend for the student and lecturer dashboards, with the Go backend serving a JSON API.
Outcome
Three universities ran on the same codebase without access to one another’s data. A fourth took about two weeks to add; most of that time went to content migration.
What I'd do differently: the multi-tenant query pattern is great until someone forgets the WHERE tenant_id = ? clause. I'd add a per-tenant test fixture and a lint rule (or a query wrapper that makes tenant scoping mandatory) from the start. We caught the omissions in code review, but a belt-and-suspenders approach would have been safer.