Be Educated
The full platform for a JEE/NEET coaching institute: enrollment, fee payments, exams, and dashboards for five roles. I built it solo as a freelance project.

Highlights
- Built solo, from the database schema to the admin dashboards.
- Payments can't get lost: the Cashfree webhook activates the enrollment even if the student never makes it back to the site.
- Access control lives in the database (Supabase row-level security), not just in the UI.
- Came back later to harden it: webhook signature checks, server-side exam timing, idempotent payment verification, rate limits on payments, plus unit tests and CI.
Overview
Be Educated is a coaching institute for JEE/NEET and foundation classes. I built their whole system: the public site, student enrollment, batches, course content, fee collection with installments and coupons, an online exam engine, announcements, and automatic fee reminders. There are five kinds of users (admin, student, parent, teacher, batch manager) and each one gets its own dashboard and only sees their own data.
Problem
The institute was managing fees, batches, tests and parent updates with spreadsheets and manual work, and none of it was connected.
Approach
Express + TypeScript backend, split into one module per area (fees, exams, content, and so on), on Supabase Postgres. Clerk handles login, and a webhook keeps Clerk users in sync with the database. Instead of trusting the frontend I put row-level security policies in Postgres, so even a buggy route can't leak another student's data. Payments go through Cashfree, and enrollment only activates from the verified webhook.
Architecture / how it works
- Clerk webhooks (svix-signed) create, update and delete users in Supabase. An
attachUsermiddleware also creates the row if the webhook is late, so the first login never breaks. - Cashfree
PAYMENT_SUCCESS_WEBHOOKgoes throughenrollmentService.verifyPayment(), so the enrollment gets activated even if the browser never returns toreturn_url. - Exam grading happens in SQL (
grade_mcq_response): single/multiple choice, true/false, and numerical answers with a tolerance. Ranked results are upserted. - A timer on the server auto-submits expired attempts every 2 minutes, so closing the tab doesn't stop the clock.
node-cronruns fee reminders daily at 21:00 IST. They escalate from due-in-7-days to overdue, and areminder_logtable makes sure nobody gets the same reminder twice.- Content files are served through signed URLs, and the admin API is split into sub-routers per area (fees, students, parents, teachers…).
Tech stack
Gallery





