

11 September 2026

The learning experience of an LMS — video player, progress tracking, quizzes — is the visible 10%. The backend underneath it is the unglamorous 90%: user records, course/lesson relationships, progress state that has to be accurate across devices, media storage for course content, and an API surface that a frontend can actually build against without guessing at behavior. This project, LearnPulse, is that backend.
An LMS's core entities — users, courses, lessons, enrollments, progress — sound simple until you model the relationships properly. A user's progress isn't just "percent complete" on a course; it's per-lesson completion state, which has to aggregate correctly to a course-level view, stay consistent if lesson order changes, and not silently break if a lesson gets added to a course a user already partially completed. MongoDB's document model made some of these nested relationships (a user's progress across many courses and lessons) more natural to represent than forcing them into a rigid relational schema would have.
Course content is disproportionately video and images — thumbnails, lesson videos, instructor avatars — which is exactly the kind of asset an application server shouldn't be handling directly. Cloudinary takes that off the backend's plate entirely: uploads, transformations, and delivery are handled by a dedicated media service rather than the Express app trying to be a file server on the side.
A fairly standard REST API on Express handles the CRUD and business logic — course/lesson management, enrollment, progress updates — kept deliberately unexciting on the framework side because the actual complexity here is in the data model and relationships, not in needing a more sophisticated backend framework.
Documenting the API with Swagger from the start — not after the frontend was already built against undocumented behavior — turned out to save real time. Once a frontend team (or future me) is building against an API, "what does this endpoint actually return" stops being answerable by reading the route handler and becomes answerable by reading the docs, which matters more the moment more than one person is working against the same backend.
Anyone can wire up a video player. The part that's genuinely hard to get right — and the part that breaks in painful, hard-to-debug ways if you get it wrong — is the backend keeping enrollment, progress, and content relationships consistent as the platform grows. That's what this project is actually about.
FAQ
Node.js and Express for the API layer, MongoDB for the data model, and Cloudinary for media (course videos, thumbnails, avatars).