case study · 2025
LearnHub
// runs on my machineSource
A full-featured learning management system on the MERN stack.
- MongoDB
- Express
- React
- Node.js
- Stripe
- Razorpay
- BullMQ
Problem
An LMS has to juggle payments, role-based access, progress tracking, and notifications — all reliably, across many concurrent learners.
Approach
Role-based access control gates every route. Payments run through Stripe and Razorpay with BullMQ queues handling webhooks and side-effects (certificates, emails) off the request path.
Architecture
- RBAC auth: student / instructor / admin scopes
- Stripe + Razorpay payments, BullMQ job queue for webhooks
- Course progress tracking & certificate generation
- Email notifications, Q&A, reviews, and notes modules
Results
- Reliable payment + fulfillment flow decoupled via queues
- Rich learner features without blocking the request lifecycle
Payments
Stripe + Razorpay
Queue
BullMQ
Access
RBAC
// code
learnhub.ts
// Keep checkout fast: enqueue side-effects, return immediately
router.post("/checkout", auth, rbac("student"), async (req, res) => {
const order = await Order.create({ user: req.user.id, ...req.body });
await paymentQueue.add("fulfil", { orderId: order.id }, {
attempts: 5, backoff: { type: "exponential", delay: 2000 },
});
res.status(201).json({ id: order.id, status: "processing" });
});lessons learned
- Decouple webhooks/emails into a queue so the request path stays fast.
- Idempotent job handlers make payment retries safe.
- Model roles as scopes early — retrofitting RBAC is painful.