
A multi-school management platform built with Fastify, Prisma, and React. Each school gets its own independent deployment: one API, one database, one URL, with no shared multi-tenant infrastructure. Built as a pnpm/Turborepo monorepo with a Fastify + Prisma API, a React + Vite PWA frontend, and test coverage across the stack: API tests against a real Postgres instance, web unit tests, and end-to-end flows in Playwright.
School administration across Ghana is still largely manual as academic records, grading, attendance, and fees spread across spreadsheets and disconnected tools, with no single system serving admins, teachers, parents, and students at once.
For a school, it's one portal covering the whole academic year i.e. terms and classes, student enrollment, assessments and grading, attendance, fee schedules and payments, and report cards that go from draft to published. Admins, teachers, parents, and students each get their own view into the same data, scoped to what they're allowed to see.
Some of that had to account for real constraints, not just clean data models. Younger students don't have their own email, but still need an account as grades, attendance, and report cards all reference them like anyone else. So a student without an email gets a profile with no login of its own and a generated ID, and every student needs a guardian attached who can actually log in on their behalf.
Behind the scenes, each school runs on its own deployment (its own API, own database, own URL) instead of one shared system. That keeps data isolation simple (no school_id column to get wrong in a query), but pushes complexity elsewhere: the rate limiter is just in-memory, not Redis-backed, because each deployment only ever serves one school. That wouldn't hold up shared across many schools, but it's the right amount of engineering for what this actually is. Onboarding a new school right now still means running SQL by hand; automating that is next.
Live in production, running the full academic lifecycle across a 33-model schema built over 25 migrations. Tested at every layer: ~79% API line coverage against a real Postgres instance, broad React Testing Library coverage on the web app, and Playwright E2E with a mocked backend and injected auth for fast, deterministic CI runs.