The hard part wasnever the calendar.
- PRODUCT
- ClassPortal
- ROLE
- Everything
- API
- GraphQL
- STACK
- NestJS · Next.js
- DATA
- Postgres · TypeORM

Private tutors run a business out of a spreadsheet and a chat thread.
Students in one sheet, this week's schedule in another, assignments in a chat thread, and invoicing done by hand at the end of the month from whichever of the three is least out of date. Every tool that promises to fix this is built for a school with an administrator, not for one person teaching twelve students.
But scheduling was never the difficult part. The difficult part is that a tutor is not a fixed thing: they start alone, they get busy, they take on a second tutor or join someone else's agency, and the records have to keep working across that change rather than being exported and re-entered.
Two deployables, one database.
A NestJS API exposing a single GraphQL surface, and a Next.js client that consumes it. One Postgres behind the API through TypeORM. The split is deliberate: the domain is wide enough that module boundaries are worth having, and the web client was never meant to be the only thing that talks to it.
next.js
apollo
entities
source of truth
Built around the operation most likely to go wrong.
Reassigning a student between tutors, and deleting an organisation without deleting the people in it, are the two operations that touch everything else. The shape of the schema is mostly a consequence of making those two safe.