A small MERN app used at Newton School to teach full-stack fundamentals — a College ERP where students log in with a phone number and can browse Courses, Assignments, and their Marks.
The codebase is deliberately kept small, readable, and free of clever abstractions so first-timers can trace every request from a click in the browser all the way to a MongoDB document — and back.
LMS/
├── backend/ Express + Mongoose API (Node)
│ ├── models/ Mongoose schemas — the shape of each collection
│ ├── controllers/ The functions that actually do work
│ ├── routes/ URL → controller mapping
│ ├── server.js Entry point — DB connect + middleware + mount routes
│ └── learn.md 📘 Deep-dive teaching guide for the backend
│
├── frontend/ React + Vite + Redux Toolkit
│ ├── src/
│ │ ├── pages/ One component per URL
│ │ ├── components/ Reusable bits (Sidebar, ProtectedRoute)
│ │ ├── store/ Redux store + slices (state management)
│ │ ├── services/ Thin wrapper around fetch — one file per resource
│ │ ├── App.jsx Routes + layout
│ │ └── main.jsx Renders <App /> into #root, wraps in <Provider>
│ └── learn.md 📘 Deep-dive teaching guide for the frontend
│
├── fullflow.md End-to-end walkthrough of a real request
├── DATABASE.md 📘 Every collection, every relationship, curl walkthrough of a full flow, roadmap
├── flowofbackend.md 📘 A teacher's journey: create course → questions → assignment → enroll → grade
├── AUTH.md 📘 Roles (student / teacher / superadmin), JWT auth, and the permission matrix
└── README.md You are here
Reading order: README.md (this file) → AUTH.md for roles & login → DATABASE.md for the data model → flowofbackend.md for how a teacher drives it → backend/learn.md and frontend/learn.md for a code tour.
- Node.js 18+ and npm
- A MongoDB database — either a free Atlas cluster (recommended, walkthrough below) or MongoDB running locally
You have two options. Pick one.
Atlas gives you a hosted MongoDB you can reach from anywhere, and the free M0 tier is more than enough for this project.
- Create an account at https://www.mongodb.com/cloud/atlas/register.
- Create a project. Name it whatever you like (e.g.
lms-learning). - Deploy a cluster:
- Click "Build a Database".
- Choose M0 (Free).
- Pick any cloud provider and the region closest to you.
- Cluster name: leave the default or rename to
Cluster0. - Click "Create Deployment". Provisioning takes 1–3 minutes.
- Create a database user:
- When prompted (or under Security → Database Access), click "Add New Database User".
- Auth method: Password.
- Choose a username (e.g.
lmsuser) and a strong password. Save both somewhere safe — you'll paste them into the connection string in a moment. - Built-in role: "Read and write to any database".
- Click "Add User".
- Whitelist your IP:
- Under Security → Network Access, click "Add IP Address".
- For development, click "Allow Access from Anywhere" (
0.0.0.0/0). This is fine for a learning project — for production you'd whitelist specific IPs only. - Click "Confirm".
- Get your connection string:
- Go back to Database → Clusters, click "Connect" on your cluster.
- Choose "Drivers" (or "Connect your application").
- Driver: Node.js, Version: latest.
- Copy the connection string. It looks like:
mongodb+srv://<username>:<password>@cluster0.abcde.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
- Fill in the placeholders:
- Replace
<username>with your DB username. - Replace
<password>with the DB password (URL-encode any special characters —@becomes%40,#becomes%23, etc.). - Optionally add a database name in the URL before the
?:The trailingmongodb+srv://lmsuser:mySecret%40123@cluster0.abcde.mongodb.net/lms?retryWrites=true&w=majority&appName=Cluster0/lmstells Mongoose to use a database namedlms. If you omit it, you'll get a default database — that's fine too.
- Replace
- Paste it into
.envin the next step.
If you'd rather not use Atlas, install MongoDB Community Edition locally. On macOS:
brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-communityYour connection string will then be:
mongodb://127.0.0.1:27017/lms
cd backend
npm install
cp .env.example .env # then edit .env with the connection string from Step 1
npm start # runs node server.js on http://localhost:9000Your .env should look like:
MONGODB_URI=mongodb+srv://lmsuser:mySecret%40123@cluster0.abcde.mongodb.net/lms?retryWrites=true&w=majority&appName=Cluster0
PORT=9000
If the server prints Connected TO DataBase you're good. If it prints an error, check:
- Password URL-encoded? (
@→%40,#→%23) - IP whitelisted in Atlas?
- Username / password typed correctly?
- Cluster still provisioning? (wait ~2 min)
cd frontend
npm install
npm run dev # runs Vite on http://localhost:5173Open http://localhost:5173 in your browser. The frontend expects the backend at http://localhost:9000 — if you change that, edit frontend/src/services/api.js.
Your fresh MongoDB is empty — nothing to log in with, no courses to browse. The backend ships with a seeder that fills all six collections with realistic connected data (teachers, students, courses, question bank, assignments, a graded submission, marks). It uses your own MONGODB_URI from .env — nothing about it is hardcoded.
cd backend
npm run seedYou'll see something like:
→ Connecting to MongoDB…
✅ Connected.
→ Clearing existing data in the six collections…
→ Inserting users…
→ Inserting courses…
→ Inserting question bank…
→ Inserting assignments…
→ Inserting a graded submission for Aria on the DSA homework…
→ Inserting marks rows…
✅ Seed complete. Log in on the frontend with any of these:
ROLE PHONE PASSWORD
superadmin 9000000000 admin
teacher 9000000001 teach (Prof. Rao)
student 9999999001 demo (Aria)
...
The seeder creates one of each role — a superadmin, two teachers, three students. Log in as any of them and you'll see a fully-populated dashboard with their role shown in the sidebar. See AUTH.md for what each role can do.
Warning:
npm run seedwipes all six collections in your database before inserting. Run it on an empty database or a scratch one — not on real data you care about.
Prefer to make your own account by hand instead? Register a student (passwords are hashed automatically):
curl -X POST http://localhost:9000/user/register \
-H "Content-Type: application/json" \
-d '{"name":"You","email":"you@x.com","password":"test","phoneNumber":9999999999}'For roles, login, and the full permission matrix, see AUTH.md. For a full walkthrough of creating courses, questions, assignments, submissions, and grades via curl, see DATABASE.md §5.
| Layer | Tool | Why it's here |
|---|---|---|
| Frontend UI | React 19 | Component-based UI |
| Frontend build | Vite | Fast dev server + build |
| Routing | React Router 7 | Client-side navigation |
| State management | Redux Toolkit | Predictable, DevTools-friendly global state |
| Backend runtime | Node.js | JavaScript on the server |
| Backend framework | Express 5 | HTTP routing + middleware |
| Database | MongoDB | Document store — flexible for teaching |
| ODM | Mongoose | Schemas + query helpers for Mongo |
| Auth | jsonwebtoken + bcryptjs | JWT login + hashed passwords, role-based access (AUTH.md) |
| Config | dotenv | Loads secrets from .env |
| Fonts | Plus Jakarta Sans + Inter (Google Fonts) | Headings + UI |
If any of these are new, go through them in order — later topics depend on earlier ones. The linked chapter names refer to the MDN Web Docs and each library's official docs, which are the ground truth.
- HTML basics — elements, attributes, forms
- CSS basics — the box model, flexbox, grid, custom properties (CSS variables), media queries, pseudo-elements (
::before/::after) - JavaScript essentials
let/const, functions, arrow functions- Objects, arrays, destructuring, spread/rest
if/for/map/filter/reduce- Promises,
async/await,try/catch - ES modules —
import/export
- JSON — how objects serialize over the network
- The browser — the DOM, dev tools, the network tab,
localStorage
- Request/response lifecycle
- HTTP verbs:
GET,POST,PUT,PATCH,DELETE— when to use which - Status codes:
200,201,400,401,404,500 - Query strings vs. request body vs. URL params
- CORS — why the browser blocks cross-origin requests, and how the
corspackage fixes it - The
fetchAPI and how we wrap it infrontend/src/services/api.js
- What Node.js is and how
require/module.exportswork npmandpackage.json— scripts, dependencies,node_modules- Environment variables and
dotenv - Express fundamentals:
app.use(...)— middlewareapp.get / post / put / delete— route handlers- The
(req, res)handler signature req.body,req.query,req.params— three input sourcesres.json(),res.status()
- The MVC-ish pattern: routes → controllers → models
- What a document database is (vs SQL)
- Collections and documents
- MongoDB Atlas setup, IP whitelist, connection string
- Mongoose schemas — types,
required,default,enum - CRUD via a model:
Model.find,.findOne,.findById,.save,.updateOne,.deleteOne,.insertMany - References across collections —
ObjectId+ref(seemodels/Marks.model.js)
- Components as functions returning JSX
- Props (read-only inputs to a component)
- State with
useState— never mutate; always set new values - Effects with
useEffect— the dependency array, when it runs - Conditional rendering, list rendering with
key - Controlled inputs (see
LoginPage.jsx) - One-way data flow: data down, events up
- Component composition (
LayoutwrapsSidebar+ page)
<BrowserRouter>,<Routes>,<Route><Link>,<NavLink>— client-side navigation without a full reloaduseNavigate()— programmatic navigation<Navigate to=... />— redirects (seeProtectedRoute.jsx)
- Why Redux — one predictable source of truth for global state, DevTools time-travel debugging, avoids prop drilling
- Core concepts: store, actions, reducers, selectors
configureStore— seesrc/store/index.jscreateSlice— bundles initial state + reducers + auto-generated action creatorscreateAsyncThunk— for async work (e.g. login API call). Auto-dispatches.pending/.fulfilled/.rejecteduseSelector— read state in a componentuseDispatch— send actions from a component<Provider store={store}>— makes the store available to the whole tree (seemain.jsx)- All of the above lives in
src/store/authSlice.js. Read that file top-to-bottom.
- Git:
clone,add,commit,push,pull, branches, PRs - ESLint — how it's configured here (
eslint.config.js) - Dev tools: React DevTools and Redux DevTools browser extensions — install both
- Get comfortable with HTML / CSS / JS in isolation
- Build a tiny Node + Express "Hello world" API
- Add MongoDB + Mongoose to it
- Learn React on its own with a small side project
- Add React Router
- Now open this repo — you'll recognize every pattern
- Then read
backend/learn.mdandfrontend/learn.mdfor the guided walkthrough
Contributions are welcome — this is a teaching repo, so clarity beats cleverness.
- One PR, one idea. Don't mix a bug fix with a refactor.
- Keep it simple. If a beginner can't read your code in one pass, it's too clever.
- Match the existing style. The two
learn.mdfiles describe our patterns — follow them. - No new dependencies without discussing first. Every dependency is another thing students have to learn.
- Don't reformat files you didn't change. Diffs should show intent, not whitespace churn.
You don't need to fork — as a contributor you work on a branch and open a
Pull Request. Never commit directly to main.
# 1. Get the latest main
git clone https://github.com/shubhIam-dev/LMS.git # first time only
cd LMS
git checkout main
git pull origin main
# 2. Create your own branch (name it after what you're doing)
git checkout -b yourname/register-page
# 3. Set up and run the app (see "Running it locally" above)
# backend: cd backend && npm install && npm run seed && npm start
# frontend: cd frontend && npm install && npm run dev
# 4. Make your change, then commit (explain the WHY)
git add .
git commit -m "Add a register page wired to /user/register"
# 5. Push your branch
git push -u origin yourname/register-pageThen on GitHub click "Compare & pull request", target main, and describe:
- What the change does
- Why it's needed
- What you tested manually (screenshots for UI changes are gold)
Keep your branch up to date with git pull origin main and merge/rebase if
main has moved on.
Browse the Issues tab — tasks are
labelled good first issue, frontend/backend/docs, and
difficulty:easy|medium|hard. Comment on one to claim it before starting.
Maintainers: to populate the Issues tab with the starter tasks, run
bash scripts/create-issues.shonce (needs the GitHub CLI:gh auth login).
The roadmaps in AUTH.md, DATABASE.md §7, and flowofbackend.md list larger features worth building.
Open an issue with:
- What you did (steps to reproduce)
- What you expected
- What actually happened
- Screenshots or console output if relevant
- Your Node / npm / OS versions
Open an issue first before writing code. Explain the use case in one paragraph — we'll discuss scope before you spend time on it.
This is a teaching repository. Do whatever helps you learn.