← Work
03
PersonalFull-stack · 2025

Learning Management System

The problem.

Every LMS demo I've seen skips the parts that make an LMS actually work. Course pages are easy. Video upload is easy. The parts that decide whether a real learning platform is trustworthy — server-side quiz scoring, video files that can't be shared, certificates that mean something, three role systems that don't leak permissions — those are the parts most tutorials wave past.

I built this one from the schema up. Eleven Postgres tables with proper relationships, JWT auth with role-based access for students, instructors, and admins, video processing through FFmpeg, and files stored in Cloudflare R2 behind signed URLs so the only way to watch a video is through the app. Quiz scoring lives on the server, so nothing about a score can be tampered with client-side. Certificates are generated by PDFKit only when progress crosses the threshold.

The frontend mirrors the backend's roles: a student never sees the instructor's draft page, an instructor never sees the admin's approval queue. Uploads report real progress. Video streams through the signed-URL layer so links can't be shared past their expiry. The whole thing runs in Docker Compose — one command to bring the stack up, one command to seed it, so the demo is never a shell.

A learning platform earns trust in the parts users never see — the quiz scorer, the signed URL, the role guard.
InstructorUploadFFmpegR2Signed URLPlayer
Signed-URL video pipeline — R2 stores it, the app streams it, links expire
Node + Express + Postgres·11 tables · 3 roles·Sequelize + Multer + FFmpeg·Cloudflare R2 + signed URLs·React + Vite + Zustand
Instructor dashboard — course draft to published pipeline
Instructor dashboard — course draft to published pipeline

Building an LMS from scratch teaches you that the interesting parts aren't the ones people click on. Nobody sees the signed-URL layer. Nobody sees the quiz scorer refusing to trust the client. Nobody sees the role guard. But those are the parts that decide whether the platform can be trusted with a real course. Next time I'd start with them — schema, permissions, and the signature parts of trust — before writing a single component.