From modern JavaScript fundamentals, through Node.js and Express, into MongoDB
persistence, and out to a working React frontend — one running example, start to finish:
a Student Management Application.
MongoDB & Mongoose — schemas, models, real persistence (6 chapters)
Part IV
React — components, hooks, calling the API (7 chapters)
Aligned to Syllabus: JavaScript Foundation, Backend Development (Node.js, Express & MongoDB), and Frontend Development using React
Prepared for Sanjay Jaiswar August 2026
Before You Begin
About This Tutorial
This tutorial takes you from modern JavaScript itself all the way to a complete MERN
stack application. Every chapter is short — a plain‑language idea, then runnable code — and everything
builds around a single running example, a Student Management System, so concepts accumulate
instead of resetting each chapter.
What You Will Build
Part 0 gets your JavaScript up to the level MERN code assumes. By the end of Part I you will have
written a native HTTP server using only Node's built‑in modules. By the end of Part II you will have a
modular, controller‑based Express REST API exposing full GET / POST / PUT / DELETE
endpoints. Part III swaps the in‑memory array for real MongoDB persistence with Mongoose. Part IV builds
a React frontend that lists, adds, edits, and deletes students through that exact API — the complete
MERN loop, working end to end.
Prerequisites
None beyond curiosity — Part 0 starts from plain JavaScript basics.
Node.js installed — v18 or later, verified with node -v.
MongoDB installed — locally, or a free MongoDB Atlas cluster, for Part III.
A code editor — Visual Studio Code recommended.
How Each Chapter Works
Concept — the idea in plain language, a few sentences, before any code.
Practical — a complete, copy‑runnable example file.
Try It Yourself — a small extension exercise to type on your own.
Occasional Common Pitfall notes flag mistakes beginners typically make.
Reference Texts
Web Development with Node and Express — Ethan Brown (O'Reilly) · Node.js Documentation
(nodejs.org) · Express.js Documentation (expressjs.com) · MDN JavaScript Guide (developer.mozilla.org)
Before You Begin
Installation & Environment Setup
There are two ways to get Node.js and MongoDB running on your machine — install them
directly (native), or run them inside Docker containers. Both work on Windows and macOS.
This section covers both, with the benefits and drawbacks of each so you can choose deliberately instead
of guessing.
Two Paths to the Same Result — a Running Node Server Connected to MongoDB
🪟 🍎
Path A · Native Install
📦Install Node.js from nodejs.org / Homebrew / winget
↓
🍃Install MongoDB Community Server directly
🐳
Path B · Docker
🖥️Install Docker Desktop (Windows or Mac)
↓
🍃docker run mongo — zero manual install
↓
🚀 node app.js → connects to MongoDB on localhost:27017
Path A — Native Install: Node.js
Windows
Download the LTS installer from nodejs.org and run the .msi — Next,
Next, Finish.
Or, if you have winget (built into Windows 11):
PowerShellBash
__CODE_S1__
macOS
Download the .pkg installer from nodejs.org, or install via Homebrew:
TerminalBash
__CODE_S2__
Verify on either OS:
TerminalBash
__CODE_S3__
A Code Editor: Visual Studio Code
Download VS Code from code.visualstudio.com — the same installer page works for both
Windows (.exe) and macOS (.zip, then drag to Applications). Once installed,
open any project folder with File → Open Folder, and use the built‑in terminal
(Terminal → New Terminal) to run every node/npm command in this
tutorial without leaving the editor.
Path A — Native Install: MongoDB
Windows — download "MongoDB Community Server" from mongodb.com, run the installer, and choose
"Install as a Service" so it starts automatically. macOS — install via Homebrew:
TerminalBash
__CODE_S4__
✅ Benefits
Fastest performance — no virtualization layer
Simplest mental model for beginners: one install, one running program
Works fully offline once installed
Easiest to debug — logs and files live in normal, findable places
⚠️ Drawbacks
Installer steps differ by OS, so instructions fork
Version conflicts if two projects need different Node/Mongo versions
MongoDB runs as a background service, using RAM even when idle
Uninstalling cleanly (especially MongoDB's data files) can be messy
Path B — Docker (Both OSes, Same Steps)
Concept
Docker packages an application with everything it needs into a container — an isolated
environment that runs identically no matter what's installed on the host machine. Instead of
installing MongoDB itself, you run a ready‑made MongoDB image in a container.
Install Docker Desktop — same download page for Windows and Mac at docker.com. On Windows
it will prompt you to enable WSL2 (Windows Subsystem for Linux) the first time; accept it.
Verify the install:
TerminalBash
__CODE_S5__
Run MongoDB — no installation needed at all:
TerminalBash
__CODE_S6__
This pulls the official MongoDB image, runs it in the background
(-d), maps container port 27017 to your machine, and stores data in a named volume so
it survives restarts.
Optional — Containerizing the Express App Too
For local learning, running only MongoDB in Docker (and Node natively) is enough. For a setup that
matches production, containerize the app itself as well:
DockerfileDocker
__CODE_S7__
Then define both services together with Docker Compose:
docker-compose.ymlYAML
__CODE_S8__
TerminalBash
__CODE_S9__
✅ Benefits
Identical environment on every machine — no "works on my machine"
MongoDB needs zero manual install or configuration
docker rm removes everything cleanly, no leftover files
Matches how the app will likely be deployed in production
Run multiple MongoDB/Node versions side by side without conflicts
⚠️ Drawbacks
An extra layer to learn — images, containers, volumes, compose
Docker Desktop uses noticeable RAM/CPU even when idle
Windows setup depends on WSL2, which can itself need troubleshooting
Slower file syncing on Mac for bind‑mounted project folders
Overkill for a single beginner script or a quick class exercise
Comparison
Native Install
Docker
Setup time
Fast (Node), moderate (MongoDB)
Slower first pull, instant after
Beginner friendliness
Easier to see what's happening
One more abstraction to learn
Consistency across machines
Can vary by OS/version
Identical everywhere
Resource usage
Lower
Higher (virtualization)
Cleanup
Manual, can leave residue
docker rm is complete
Best for
Learning fundamentals, solo scripts
Team projects, matching production
Recommendation for This Tutorial
Install Node.js natively — every chapter assumes a local node command. For MongoDB,
either is fine: a native install works, and the one‑line docker run mongo command above
is arguably less setup for a beginner, since it skips the installer entirely. Save full
docker-compose setups for once the fundamentals in Parts I–IV feel comfortable.
Contents
Table of Contents
Start Here
About This Tutorial · Installation & Environment Setup (Windows, macOS, Docker)
Part 0 — JS Foundations for MERN
What Is Modern JavaScript (ECMAScript)? · 01 Variables & Operators · 02 Functions &
Control Flow · 03 Arrays & map/filter/reduce · 04 Objects & Destructuring · 05 Modern ES6+
Essentials · 06 Modules (require/export)
Part I — Core Node.js Runtime
What Is Node.js? · 07 Built‑in Modules · 08 File System (fs) · 09 Async Programming ·
10 Events & EventEmitter · 11 The HTTP Module · 12 Manual Routing & Its Limits
What Is MongoDB? · 22 Connecting with Mongoose · 23 Schema & Model · 24 Create & Read ·
25 Update & Delete · 26 Data Validation · 27 Controller + MongoDB
Part IV — React Frontend
28 Setup & JSX Basics · 29 Components & Props · 30 State & Events ·
31 Fetching Data (useEffect) · 32 Forms & POST Requests · 33 Update & Delete from UI ·
34 React Router & Integration
Part 0
Modern JavaScript Foundations for MERN
The exact slice of JavaScript that MERN code leans on constantly — before we touch Node or Express
at all.
Introduction
What Is Modern JavaScript (ECMAScript)?
"JavaScript" is the language; ECMAScript is the specification that defines what
the language is allowed to do. When people say "modern JavaScript," they mean code written using
ECMAScript features from 2015 onward — and that's exactly the style every chapter in this tutorial
uses, on both the frontend and the backend.
A Short History
Version
Year
What Changed
ES5
2009
The "old style" — var, plain functions, no built‑in module system
ES6 / ES2015
2015
The big rewrite — let/const, arrow functions, classes, Promises, template literals, destructuring, modules
ES2016 – ES2024
Yearly
Smaller, steady additions — async/await (2017), spread/rest (2018), optional chaining & nullish coalescing (2020), and more each year
Since 2015, a new ECMAScript version ships every year with a handful of additions, rather than one
huge rewrite. "Modern JavaScript" or "ES6+" just means: assume all of that is available.
Why It Matters for MERN
Node.js (Part I) and every modern browser implement ECMAScript directly, so ES6+ syntax runs with no
extra setup. React (Part IV) goes a step further and uses a build tool (Vite) to support even newer,
experimental syntax like JSX. This is also why Node's module system, require()
(Chapter 06), predates ES6 and looks different from the import syntax you'll use in React —
two different module systems from two different eras of JavaScript, both still in daily use.
Practical — Old Style vs. Modern Style
The same small program, written both ways:
old-style.js (ES5)JavaScript
__CODE_E1__
modern-style.js (ES6+)JavaScript
__CODE_E2__
Both produce identical output. The ES6+ version is what the rest of this tutorial — and virtually
all MERN code you'll encounter — actually looks like.
Use const by default and let only when a value needs to change — avoid
var. Template literals (backtick strings) let you drop variables straight into text with
${...} instead of joining strings with +.
Practical — Student Marks
chapter-01/basics.jsJavaScript
__CODE_J1__
Run it:
TerminalBash
__CODE_J2__
Try It Yourself
Add a new const attendance = 82; and print a template literal that includes both marks
and attendance in one sentence.
Part 0 · JS Foundations
02 Functions & Control Flow
Concept
A function is a reusable block of logic. Arrow functions ((x) => x * 2) are a shorter
way to write the same thing and are what you'll see most in Express and React code. Combine functions
with if/else and loops to make decisions over data.
Practical — Grading Students
chapter-02/grades.jsJavaScript
__CODE_J3__
Run it:
TerminalBash
__CODE_J4__
Try It Yourself
Rewrite the for loop as a for...of loop: for (const s of students) { ... }
— same result, less bookkeeping.
Part 0 · JS Foundations
03 Arrays & Array Methods
Concept
map, filter, and reduce are the array toolbox you will use
constantly — map transforms every item, filter keeps only the ones that pass
a test, and reduce combines everything into one value. This is exactly how you'll shape
API data and render lists in React later.
Practical — Working With a Student List
chapter-03/arrays.jsJavaScript
__CODE_J5__
Run it:
TerminalBash
__CODE_J6__
Try It Yourself
Chain filter then map in one line to get just the names of students
enrolled in "Node.js" who also passed.
Part 0 · JS Foundations
04 Objects & Destructuring
Concept
Objects group related data with named keys. Destructuring pulls specific fields straight out of an
object into variables — this is exactly the pattern you'll use for req.body in Express and
props in React.
Practical — Building a Student Object
chapter-04/objects.jsJavaScript
__CODE_J7__
Run it:
TerminalBash
__CODE_J8__
Try It Yourself
Destructure with a default value: const { name, grade = "Not graded" } = student; —
then remove grade from the object and confirm the default kicks in.
Part 0 · JS Foundations
05 Modern ES6+ Essentials
Concept
A quick tour of small features you'll see everywhere in MERN code: the spread operator
(...obj) copies/merges objects, rest parameters collect extra function arguments
into an array, optional chaining (?.) safely reads nested data without crashing,
and nullish coalescing (??) supplies a fallback only when a value is
null/undefined.
Practical — Spread, Rest & Safe Access
chapter-05/modern.jsJavaScript
__CODE_J9__
Run it:
TerminalBash
__CODE_J10__
Common Pitfall
|| falls back on any falsy value (0, "", false),
while ?? only falls back on null/undefined. A student with
marks: 0 would incorrectly become the fallback with || — ??
gets it right.
Part 0 · JS Foundations
06 Modules — require & export
Concept
Splitting code across files keeps it manageable. Node uses the CommonJS pattern —
module.exports to share code, require() to pull it in — which is exactly what
every Node and Express example in this tutorial uses.
Practical — A Small Math Utility Module
chapter-06/mathUtils.jsJavaScript
__CODE_J11__
chapter-06/app.jsJavaScript
__CODE_J12__
Run it:
TerminalBash
__CODE_J13__
Where This Leads
Later, in React, you'll write import { add } from "./mathUtils" instead — same idea
(splitting code into reusable pieces), different syntax (ES Modules instead of CommonJS).
Part I
Core Node.js Runtime
Understanding how Node talks to the operating system, the file system, and the network — before any
framework gets involved.
Introduction
What Is Node.js?
JavaScript was originally built to run inside a browser only. Node.js takes the same
language and lets it run directly on a computer or server — no browser required — which is what makes
JavaScript usable for backend development at all.
Why It Matters
Node runs on Chrome's V8 engine and adds capabilities a browser deliberately blocks for security —
reading files, opening network servers, talking to databases. It does this using a
non‑blocking, event‑driven model: instead of waiting for a slow task (like a file read or a
database query) to finish before moving on, Node starts the task, keeps working, and comes back to it
via a callback once it's done. That's the same idea behind async/await from Chapter 09.
Practical — Your First Node Script
Node has no HTML page to load into — you just run a JavaScript file directly from the terminal.
hello.jsJavaScript
__CODE_N1__
TerminalBash
__CODE_N2__
npm — Node's Package Manager
Node ships with npm, which installs reusable packages from a public registry —
npm install express in Chapter 13 is exactly this. It's how every third‑party library in
this tutorial (Express, Mongoose, Axios) gets added to a project.
Part I · Core Node.js
07 Node.js Built‑in Modules
Concept
Node comes with ready‑made modules you can use without installing anything — os,
path, fs, events, and http are the ones you'll use
most. Load any of them with require(), the module pattern from Chapter 06.
Practical — Exploring the OS Module
Create chapter-07/app.js to inspect the hardware and runtime environment your script is
running on:
chapter-07/app.jsJavaScript
__CODE_1__
Run it:
TerminalBash
__CODE_2__
Try It Yourself
Add os.hostname() and os.homedir() to the output. Then use
path.join(__dirname, "data", "students.json") and log the result — notice how
path.join normalizes separators regardless of operating system.
Part I · Core Node.js
08 The File System Module (fs)
Concept
The fs module lets Node read and write files on disk. We'll use its
promises version with async/await, because it's the simplest way to read a
file, wait for the result, and then use it.
Practical Project — Student File Management System
Build a small CRUD tool that stores and reads data from a local students.json file.
chapter-08/app.jsJavaScript
__CODE_3__
Run it:
TerminalBash
__CODE_4__
Common Pitfall
Forgetting await on an fs.promises call returns a pending
Promise object instead of your data — a frequent source of "why is this
[object Promise]" bugs for beginners.
Part I · Core Node.js
09 Asynchronous Programming
Concept
Some tasks — like fetching data or reading a file — take time. JavaScript doesn't freeze while
waiting; it moves on and comes back once the task is done. A Promise represents "a value
that will arrive later," and async/await lets us write that waiting code so it reads
top‑to‑bottom, just like normal code.
Practical — Simulated Academic Record Fetcher
A script that chains three mock asynchronous services — mirroring what a real API call to a database
eventually looks like.
chapter-09/app.jsJavaScript
__CODE_5__
Run it:
TerminalBash
__CODE_6__
Try It Yourself
Rewrite fetchCompleteStudentRecord using Promise.all() so
getCourse is not waited on before it can start — then think about why, in this
particular case, it still has to wait for getStudent first (its input depends on the
result).
Part I · Core Node.js
10 Events & EventEmitter
Concept
Think of EventEmitter like a notice board. Listeners "subscribe" to an event with
.on(). Later, when something happens, we .emit() that event — and every
listener reacts, without the two pieces of code needing to know about each other directly.
Practical — Student Registration Event System
An event fires once when a student registers; three independent listeners react to it.
chapter-10/app.jsJavaScript
__CODE_7__
Run it:
TerminalBash
__CODE_8__
Try It Yourself
Add a fourth listener that only reacts once using registry.once(...), then call
registerStudent twice and observe that the "once" listener fires only on the first call.
Part I · Core Node.js
11 The HTTP Module
Concept
Node includes a built‑in http module that turns your script into a web server —
listening for incoming TCP requests and writing responses directly, with no framework involved.
Practical — A Native HTTP Server
chapter-11/server.jsJavaScript
__CODE_9__
Run it, then open your browser or run curl http://localhost:3000. Press
Ctrl + C to stop the server.
TerminalBash
__CODE_10__
Part I · Core Node.js
12 Manual Routing & Its Limits
Concept
With the raw http module, routing means manually inspecting req.url and
req.method inside a chain of if / else statements — you own every detail of
request parsing and response formatting.
Practical — Manual Routing via Native HTTP
chapter-12/server.jsJavaScript
__CODE_11__
The Problem with Raw HTTP
Imagine 100+ routes, manual request‑body parsing, query‑string parsing, and error handling — all as
nested if/else chains on req.url. It becomes unmaintainable fast. This exact
limitation is why Express.js exists — the subject of Part II.
Part II
The Express.js Framework
Routing, middleware, and clean architecture — turning the raw HTTP concepts from Part I into a real,
maintainable REST API.
Part II · Express.js
13 Introduction to Express
Concept
Express is a web framework — a library built on top of Node's http module that
gives you simple, ready‑made tools for routing, middleware, and sending responses. It's not a
replacement for Node; it's Node's http module (Chapter 11) with the repetitive parts —
the if/else chains from Chapter 12 — already solved for you. It's the most widely used
Node web framework, which is why "MERN" names it directly.
Practical Steps
Initialize a new project:
TerminalBash
__CODE_12__
Install Express:
TerminalBash
__CODE_13__
Create app.js:
chapter-13/app.jsJavaScript
__CODE_14__
Run it:
TerminalBash
__CODE_15__
Part II · Express.js
14 Express Routing
Concept
Express maps HTTP methods to handler functions directly on the app instance —
app.get(), app.post(), app.put(), app.delete() —
instead of branching on req.url manually.
Practical — Defining Student Endpoints
Create chapter-14/app.js:
chapter-14/app.jsJavaScript
__CODE_16__
Try It Yourself
Test every route with curl or Postman: GET/students, GET/students/7,
POST/students, PUT/students/7, DELETE/students/7.
Part II · Express.js
15 Request Parameters
Concept
Clients send data to your API in three distinct ways, and Express exposes each one on the request
object:
Source
Property
Example
Route parameters
req.params
/students/10
Query parameters
req.query
/students?course=node&sort=asc
Request body
req.body
JSON payload of a POST/PUT
Practical — Extracting Parameters
chapter-15/app.jsJavaScript
__CODE_17__
Part II · Express.js
16 Middleware
Concept
A middleware is just a function that runs before your route handler. It gets
(req, res, next) and must call next() to pass control onward — that's how
logging, authentication, and body‑parsing all get "plugged in" to Express.
Practical — Writing Custom Logging Middleware
chapter-16/app.jsJavaScript
__CODE_18__
Common Pitfall
Forgetting to call next() inside a middleware function leaves the request hanging
forever — the client will simply time out with no response and no error.
Part II · Express.js
17 Parsing JSON Request Bodies
Concept
Express does not parse incoming JSON payloads by default. Adding the built‑in
express.json() middleware is what makes req.body available at all.
Practical — Handling JSON Payloads
chapter-17/app.jsJavaScript
__CODE_19__
Try It Yourself
Send a POST to /api/students in Postman with an empty
body, then with only {"name": "Asha"}, then with a full payload — observe the 400 response
and the 201 response.
Part II · Express.js
18 REST API Design
Concept
REST (Representational State Transfer) is an architectural style for stateless client‑server
communication, where standard HTTP methods map to actions on named resources.
Standard REST Endpoints & Status Codes
Method & Path
Action
Typical Response
GET/api/students
Retrieve all students
200 OK
GET/api/students/:id
Retrieve one student
200 OK · 404 Not Found
POST/api/students
Create a new student
201 Created · 400 Bad Request
PUT/api/students/:id
Update a student
200 OK · 404 Not Found
DELETE/api/students/:id
Remove a student
200 OK · 204 No Content
Design Principle
URLs name resources (nouns — /students), never actions (verbs — avoid
/getStudents). The HTTP method already supplies the verb.
Part II · Express.js
19 CRUD API (In‑Memory)
Concept
Before introducing a real database, building CRUD against an in‑memory array is the fastest way to
master API logic — array methods like find, filter, and findIndex,
paired with correct HTTP status codes for every outcome.
Practical — In‑Memory Student CRUD API
chapter-19/app.jsJavaScript
__CODE_20__
Try It Yourself
Add a PATCH/api/students/:id route that updates
only the fields present in the request body, unlike PUT which is
expected to replace the whole resource.
Reference
Testing APIs With Postman
A browser can only send GET requests by typing a URL.
To test POST, PUT, and
DELETE routes — the rest of the CRUD API you just built in
Chapter 19 — you need a tool built for it. Postman is the standard choice.
Practical — Testing the Student CRUD API
Start the server: node chapter-19/app.js.
Open Postman, click New → HTTP Request.
Pick a method from the dropdown, enter the URL, and click Send. For a body (POST/PUT), open
the Body tab → select raw → choose JSON from the type dropdown → type the JSON
payload.
Method
URL
Body (raw JSON)
Expect
GET
localhost:3000/api/students
—
200, array of students
POST
localhost:3000/api/students
{"name":"Zara","course":"MongoDB"}
201, created student
PUT
localhost:3000/api/students/1
{"marks": 95}
200, updated student
DELETE
localhost:3000/api/students/1
—
200, deletion message
Try It Yourself
Send the POST request with the Content-Type header
missing or wrong (Postman sets it automatically when you pick raw → JSON, but try overriding
it) — watch req.body come back empty, tying directly back to Chapter 17's
express.json() middleware.
Reading the Response
Postman shows the status code (top right of the response panel) and the response body
below it. A 404 on a valid‑looking ID usually means you deleted that record in an earlier
test — restart the server to reset the in‑memory array back to its starting two students.
Part II · Express.js
20 express.Router
Concept
As an application grows, keeping every route in one app.js file becomes unmanageable.
express.Router creates modular, mountable route handlers that keep each resource's routes
in their own file.
Practical — Organizing Routes
Project structure:
StructureText
__CODE_21__
Create routes/studentRoutes.js:
routes/studentRoutes.jsJavaScript
__CODE_22__
Mount it in app.js:
chapter-20/app.jsJavaScript
__CODE_23__
Part II · Express.js
21 Controllers
Concept
Separation of concerns means routing definitions should not contain business logic. Controllers sit
between routes and data, so routes/ files stay a clean map of URL → handler, while
controllers/ files hold the actual behavior.
Practical — Adding a Controller Layer
Updated project architecture:
StructureText
__CODE_24__
Create controllers/studentController.js:
controllers/studentController.jsJavaScript
__CODE_25__
Update routes/studentRoutes.js to delegate to it:
routes/studentRoutes.jsJavaScript
__CODE_26__
Where This Leads
This exact routes → controller → data shape is what carries forward once the
in‑memory array is replaced by a real MongoDB model — only the inside of the controller functions
changes, starting right now in Part III.
Part III
MongoDB & Mongoose
Swapping the in‑memory array for a real database — schemas, models, and persistent CRUD, using the
exact same routes and controllers you already built.
Introduction
What Is MongoDB?
MongoDB is a NoSQL, document‑based database. Instead of rows and tables like SQL
databases, it stores data as JSON‑like documents grouped into collections — a natural fit for
JavaScript, since a document looks just like the objects you already work with in Chapters 04 and 08.
SQL Term
MongoDB Equivalent
Database
Database
Table
Collection
Row
Document
Column
Field
Practical — Basic Commands in the Mongo Shell
Install MongoDB Community Server, start it, then open mongosh (the MongoDB shell) to try
create, read, update, and delete directly — before any Node code is involved at all.
mongoshShell
__CODE_N3__
Try It Yourself
Run db.students.find({ course: "Node.js" }) to filter documents by a field — notice
how similar this reads to the .filter() array method from Chapter 03.
Where Mongoose Fits
Everything above works with plain MongoDB and no Node code at all. Mongoose (starting in
Chapter 22) is a library that lets you run these same operations from inside Express — with schemas
and validation on top, instead of writing raw shell commands by hand.
Part III · MongoDB
22 Connecting with Mongoose
Concept
Mongoose is a library that connects Node to MongoDB and adds structure on top of it — schemas,
models, and validation. It's the standard way MERN apps talk to MongoDB.
Practical — Connecting to a Local Database
Install Mongoose:
TerminalBash
__CODE_M1__
Create chapter-22/db.js:
chapter-22/db.jsJavaScript
__CODE_M2__
Call it when the server starts:
chapter-22/app.jsJavaScript
__CODE_M3__
Try It Yourself
Start MongoDB locally (mongod), run the server, and confirm
MongoDB connected successfully prints. Stop MongoDB and re‑run to see the error path.
Part III · MongoDB
23 Schema & Model
Concept
A Schema describes the shape of a document — what fields it has and their types. A
Model is built from a schema and is what you actually use to query the database, similar to how
a class is a blueprint and an instance is the real object.
Practical — The Student Model
chapter-23/models/Student.jsJavaScript
__CODE_M4__
Try It Yourself
Add a createdAt: { type: Date, default: Date.now } field, then check MongoDB Compass
(or mongosh) to see it appear automatically on new documents.
Part III · MongoDB
24 Create & Read with Mongoose
Concept
Mongoose models expose methods like .create(), .find(), and
.findById() that return Promises — used with async/await, exactly like
Chapter 09.
Practical — Replacing the In‑Memory Array
chapter-24/app.jsJavaScript
__CODE_M5__
Try It Yourself
Create a few students through Postman, then GET/api/students and notice every document now has a real MongoDB _id instead
of the counter‑based id from Chapter 19.
Part III · MongoDB
25 Update & Delete with Mongoose
Concept
findByIdAndUpdate and findByIdAndDelete mirror the CRUD shape from
Chapter 19, just backed by a real database instead of an array.
Practical — Update & Delete Routes
chapter-25/app.jsJavaScript
__CODE_M6__
Common Pitfall
Without { new: true }, findByIdAndUpdate returns the document as it was
before the update — a common source of "my update isn't working" confusion when it actually did
work.
Part III · MongoDB
26 Data Validation
Concept
Mongoose schemas can enforce rules directly — required, min/max,
and enum — so invalid data never reaches the database at all.
Practical — Validated Student Schema
chapter-26/models/Student.jsJavaScript
__CODE_M7__
Catch the validation error and respond cleanly instead of crashing:
chapter-26/app.jsJavaScript
__CODE_M8__
Common Pitfall
Skipping try/catch around a Mongoose write means a validation error becomes an
unhandled promise rejection instead of a clean 400 response to the client.
Part III · MongoDB
27 Controller + MongoDB
Concept
Bringing it together: the Router/Controller architecture from Chapters 20–21, now with every
controller function backed by the Student model instead of an array.
Practical — The Production‑Shaped Backend
controllers/studentController.jsJavaScript
__CODE_M9__
routes/studentRoutes.jsJavaScript
__CODE_M10__
Where This Leads
Your backend is now complete and production‑shaped — real persistence, validation, and clean
architecture. Part IV builds the React frontend that talks to this exact API.
Part IV
The React Frontend
Components, state, and hooks — building the interface that lists, adds, edits, and deletes students
through the API you just finished building.
Part IV · React
28 Setup & JSX Basics
Concept
React is a JavaScript library for building user interfaces out of small, reusable pieces called
components. Instead of manually updating the page with DOM calls, you describe what the UI
should look like for a given piece of data, and React handles updating the browser when that data
changes. Vite scaffolds a React project in seconds; JSX lets you write HTML‑like markup directly
inside JavaScript — under the hood it's just function calls that build UI.
Practical — Your First React Screen
TerminalBash
__CODE_R1__
src/App.jsxJavaScript
__CODE_R2__
Practical — Displaying a Computed Value in JSX
JSX isn't limited to static text — anything inside curly braces { } is plain JavaScript,
including a function call:
src/App.jsxJavaScript
__CODE_R13__
Try It Yourself
Change the heading text and save — notice the browser updates instantly without a manual refresh
(Vite's hot module reload). Then change a and b in the sum example and watch
the displayed result update too.
Part IV · React
29 Components & Props
Concept
Break UI into small, reusable components. Props pass data from a parent component into a
child — the same idea as function parameters, from Chapter 02.
Practical — A Reusable Student Card
src/components/StudentCard.jsxJavaScript
__CODE_R3__
src/App.jsxJavaScript
__CODE_R4__
Practical — Styling With a CSS File
Every component can import its own stylesheet. Class names in JSX use className instead
of HTML's class — everything else is regular CSS:
src/components/StudentCard.cssCSS
__CODE_R14__
Try It Yourself
Add a passed boolean prop and render {passed ? "Passed" : "Failed"} inside
the card using a ternary — the same ternary pattern from Chapter 02's isPassing. Then add
a .card h3 rule to the stylesheet to color the student's name.
Part IV · React
30 State & Events
Concept
State is data a component remembers between renders. useState returns a value
and a setter function; calling the setter re‑renders the component with the new value. Events like
onChange are how the UI tells state to update.
Practical — A Counter (useState in Its Simplest Form)
Before wiring state to a real feature, see the pattern on its own — a value, and a button that
changes it:
src/components/Counter.jsxJavaScript
__CODE_R15__
Practical — Searching a Student List
The same pattern, applied to something useful — state that drives what's displayed:
src/components/StudentList.jsxJavaScript
__CODE_R5__
Try It Yourself
Add a second useState for a course filter, and combine both filters in the same
.filter() call — reusing the array method from Chapter 03.
Part IV · React
31 Fetching Data with useEffect
Concept
useEffect runs code after a component renders — the right place for an API call.
Axios makes that call and returns a Promise, exactly like the async patterns from Part I and Part III.
Practical — useEffect in Its Simplest Form: a Live Clock
Before fetching anything, see useEffect on its own — running a side effect
(setInterval) once, after the component first renders:
src/components/Clock.jsxJavaScript
__CODE_R16__
Practical — Fetching a Public API
The same useEffect + axios pattern works for any API on the internet, not
just your own. JSONPlaceholder is a free public API
made for exactly this kind of practice:
src/components/PublicUsers.jsxJavaScript
__CODE_R17__
Practical — Loading Real Students from Your Own API
TerminalBash
__CODE_R6__
src/components/StudentList.jsxJavaScript
__CODE_R7__
Common Pitfall
Leaving out the empty [] dependency array makes useEffect re‑run after
every render — which triggers another fetch, which triggers another render — an infinite loop of
network requests. In the clock example, it's what would make a new setInterval start on
every render instead of just once.
Part IV · React
32 Forms & POST Requests
Concept
A controlled input keeps its value in state, updated on every keystroke via
onChange. On submit, axios.post sends that state to the Express API — the
same POST /api/students route from Chapter 24.
Practical — Add Student Form
src/components/AddStudentForm.jsxJavaScript
__CODE_R8__
Practical — Custom Validation With an Error Message
The required attribute stops an empty submit, but gives no feedback beyond the browser's
default tooltip. Real forms usually show their own message, driven by state:
Add a second check — marks must be between 0 and 100 — with its own error message, the
same way error is set for the name field.
Part IV · React
33 Update & Delete from the UI
Concept
The same pattern as adding: an event handler calls axios.put or
axios.delete, then updates local state so the screen reflects the change immediately
without a page reload.
Same idea as delete, with one addition: a local editing flag switches the card between
display mode and an inline edit form. Saving calls axios.put, then hands the updated
student back up to the list:
src/components/StudentCard.jsx (full, with editing)JavaScript
Add a loading indicator — a saving boolean state, set to true before
axios.put and back to false after — and disable the Save button while it's
true, so a slow network can't trigger a double submit.
Part IV · React
34 React Router & Full Stack Integration
Concept
React Router swaps between pages without a full browser reload. This closes the loop on the entire
MERN stack: React form → Express route → Mongoose model → MongoDB → response → React state update.
Practical — A Two‑Page Student App
TerminalBash
__CODE_R11__
src/App.jsxJavaScript
__CODE_R12__
The Complete MERN Data Flow
React form collects input → Axios POSTs it to Express → Express passes it to a
Mongoose model → Mongoose validates and saves it to MongoDB → Express responds → React updates state
and re‑renders. Every chapter in this tutorial is one link in that chain.
Well Done
You've Built a Complete MERN Stack Application
You now understand modern JavaScript, how Node.js works under the hood, how to design a
REST API with Express, how to persist and validate data with MongoDB and Mongoose, and how to build a
React frontend that talks to all of it. That's the full MERN stack, working end to end.
Next: Authentication
Add login, JWTs, and protected routes so students can securely manage their own data.
Next: State Management
Explore Context or Redux as the app's shared state grows beyond a few components.
Next: Deployment
Deploy the API and frontend, and connect to a hosted MongoDB Atlas cluster.