</> MERN. Stack Tutorial
MERN Stack Practical 34 Chapters By Sanjay Jaiswar
JavaScript · Node.js · Express.js · MongoDB · React

The Complete
MERN Stack Tutorial

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.

Modern JavaScript (ES6+)
Node.js Core Modules
Express.js & REST APIs
MongoDB & Mongoose
React Components & Hooks
Full Stack Integration
Part 0
Modern JS foundations for MERN (6 chapters)
Part I
Core Node.js — fs, http, events, async (6 chapters)
Part II
Express.js — routing, middleware, REST CRUD (9 chapters)
Part III
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

  1. Download the LTS installer from nodejs.org and run the .msi — Next, Next, Finish.
  2. Or, if you have winget (built into Windows 11):
    PowerShellBash
    __CODE_S1__

macOS

  1. 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.

  1. 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.
  2. Verify the install:
    TerminalBash
    __CODE_S5__
  3. 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
ComparisonNative InstallDocker
Setup timeFast (Node), moderate (MongoDB)Slower first pull, instant after
Beginner friendlinessEasier to see what's happeningOne more abstraction to learn
Consistency across machinesCan vary by OS/versionIdentical everywhere
Resource usageLowerHigher (virtualization)
CleanupManual, can leave residuedocker rm is complete
Best forLearning fundamentals, solo scriptsTeam 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.

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

VersionYearWhat Changed
ES52009The "old style" — var, plain functions, no built‑in module system
ES6 / ES20152015The big rewrite — let/const, arrow functions, classes, Promises, template literals, destructuring, modules
ES2016 – ES2024YearlySmaller, 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.

Where Each Feature Is Taught

let/const & template literals — Chapter 01 · arrow functions — Chapter 02 · map/filter/reduce — Chapter 03 · destructuring — Chapter 04 · spread/rest, optional chaining, nullish coalescing — Chapter 05 · modules — Chapter 06 · async/await & Promises — Chapter 09 · classes — Chapter 10.

Part 0 · JS Foundations

01 Variables, Data Types & Operators

Concept

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

  1. Initialize a new project:
    TerminalBash
    __CODE_12__
  2. Install Express:
    TerminalBash
    __CODE_13__
  3. Create app.js:
    chapter-13/app.jsJavaScript
    __CODE_14__
  4. 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:

SourcePropertyExample
Route parametersreq.params/students/10
Query parametersreq.query/students?course=node&sort=asc
Request bodyreq.bodyJSON 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 & PathActionTypical Response
GET /api/studentsRetrieve all students200 OK
GET /api/students/:idRetrieve one student200 OK · 404 Not Found
POST /api/studentsCreate a new student201 Created · 400 Bad Request
PUT /api/students/:idUpdate a student200 OK · 404 Not Found
DELETE /api/students/:idRemove a student200 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

  1. Start the server: node chapter-19/app.js.
  2. Open Postman, click New → HTTP Request.
  3. 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.
MethodURLBody (raw JSON)Expect
GETlocalhost:3000/api/students200, array of students
POSTlocalhost:3000/api/students{"name":"Zara","course":"MongoDB"}201, created student
PUTlocalhost:3000/api/students/1{"marks": 95}200, updated student
DELETElocalhost:3000/api/students/1200, 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

  1. Project structure:
    StructureText
    __CODE_21__
  2. Create routes/studentRoutes.js:
    routes/studentRoutes.jsJavaScript
    __CODE_22__
  3. 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

  1. Updated project architecture:
    StructureText
    __CODE_24__
  2. Create controllers/studentController.js:
    controllers/studentController.jsJavaScript
    __CODE_25__
  3. 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 TermMongoDB Equivalent
DatabaseDatabase
TableCollection
RowDocument
ColumnField

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

  1. Install Mongoose:
    TerminalBash
    __CODE_M1__
  2. Create chapter-22/db.js:
    chapter-22/db.jsJavaScript
    __CODE_M2__
  3. 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:

src/components/AddStudentForm.jsx (excerpt)JavaScript
__CODE_R18__

Try It Yourself

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.

Practical — Deleting a Student

src/components/StudentCard.jsxJavaScript
__CODE_R9__
src/components/StudentList.jsx (excerpt)JavaScript
__CODE_R10__

Practical — Editing a Student (Full Update Flow)

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
__CODE_R19__
src/components/StudentList.jsx (excerpt)JavaScript
__CODE_R20__

Try It Yourself

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.