// chapter 1
A new API, built to one pattern
I built the API in Fastify with one module pattern throughout (routes, controller, service, repository, schema, mapper), so every feature looks the same and is easy to hand over. It covers OTP sign-in with refresh tokens, the course catalogue, enrolled courses and videos, watch progress, live classes and attendance, notifications, notes, assignments, certificates and the home dashboard: around 40 endpoints.
Shared tables owned by the old system are read through careful SQL; new tables are modelled in Prisma. Responses are mapped so sensitive fields such as passwords and one-time codes never leave the server.
// chapter 2
The data migration
31,550
students
31,516
enrolments, every ID preserved
197,700+
exam results and result lines
The two databases sat on one server, but no account could read both, so I moved the data through staging tables: export, rename, import beside the real tables, then transform with SQL.
- IDs preserved for students and enrolments, so every relationship stayed intact (orphan check: 0).
- Real-world data, handled honestly. One phone number often belonged to several people, and some registration numbers were duplicated by double-clicks. The team agreed to keep every row and drop the unique constraints the real data broke.
- Messy dates in many formats were parsed where possible and set aside where not, with a recovery script kept for later.
- Fixes after checking. When enrolment numbers turned out to live on a different old table, I backfilled 29,434 of them from staging.
- Orders (30,000+) and results followed the same method.
// chapter 3
Live classes on Zoom
Students join live classes inside the app and the web portal through Zoom’s Meeting SDK. One signed join bundle works for web, iOS and Android.
- Only enrolled students can join, and only when the class is actually live.
- Attendance works automatically: the display name is set in the format the existing Zoom webhook expects, so attendance is recorded with no extra system.
- Classes don’t stay “live” forever when an end-of-meeting webhook is missed: the API checks the meeting’s real status with Zoom and corrects it.
- For teachers, a separate API with OTP sign-in, rotating single-use refresh tokens, a dashboard, their schedule and a host bundle to start a class from the app.
Privacy decision
Teachers see counts of enrolled students, not their personal details. The student list stays with the admin team: teachers get only what they need to teach.
// chapter 4
Results
- The mobile app and student portal run on one modern API instead of the legacy system.
- Tens of thousands of student, enrolment, order and result records moved with their links intact.
- Live classes, attendance and the teacher app share one well-tested set of rules.
- Node.js
- Fastify 5
- Prisma 6
- MySQL
- JWT
- Zoom Meeting SDK
- Zoom OAuth
- Firebase push
- Flutter