DEV Community

Ankit Silwal
Ankit Silwal

Posted on

Express JS

🎯 Overview

Welcome to "From Zero to Backend Hero", a complete hands-on roadmap designed to take you from absolute beginner to backend developer using Node.js + Express.js.

Each phase combines:

📘 Detailed concepts

🧩 Mini-project milestones

🔁 Refactor & improvement goals

💡 Optional challenges to deepen understanding

This journey is project-oriented, meaning every topic will end with code you can run, test, and improve.

🧱 Phase 1: Core Fundamentals

Goal: Understand how web servers, HTTP requests, and responses work.

📚 What You’ll Learn

What Node.js is and why Express.js makes it powerful

Setting up a Node project using

npm init -y
npm install express nodemon

Running a dev server with npm run start:dev

Understanding HTTP (requests, responses, status codes, methods)

How the browser (client) talks to your Express server

🧩 Mini Project: express-basics-api

Build a simple Express server that:

Runs on port 3000

Routes:

/ → returns "Server is running..."

/api/info → returns app info in JSON

Uses nodemon for auto restart

Reads port from environment variables (process.env.PORT)

import express from "express";
import dotenv from "dotenv";
dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => res.send("Server is running..."));
app.get("/api/info", (req, res) => res.json({ app: "express-basics-api", status: "active" }));

app.listen(PORT, () => console.log(Server listening on port ${PORT}));

💡 Improvements

Add /health route that returns system uptime and timestamp

Use dotenv for environment configuration

Enable ES Modules ("type": "module" in package.json)

🌐 Phase 2: Basic Routing & Data Retrieval (GET)

Goal: Learn Express routing and dynamic data serving.

📚 What You’ll Learn

Defining routes with app.get()

Using req and res objects

Handling route parameters (/api/users/:id)

Using query strings (?filter=name&value=john)

Handling invalid inputs (400/404 errors)

🧩 Mini Project: user-directory-api

Endpoints:

GET /api/users → return all mock users

GET /api/users/:id → return a single user

GET /api/users?filter=name&value=john → filter users by name

💡 Improvements

Validate numeric IDs

Add /api/products endpoint

Use modular functions (getAllUsers, getUserById)

✏️ Phase 3: CRUD Operations (POST, PUT, PATCH, DELETE)

Goal: Implement full CRUD — create, read, update, and delete users.

📚 What You’ll Learn

Handling POST, PUT, PATCH, DELETE requests

Using express.json() to parse JSON

Working with request payloads

Returning correct status codes

🧩 Mini Project: crud-users-api

Endpoints:

POST /api/users → Create new user

PUT /api/users/:id → Replace existing user

PATCH /api/users/:id → Update part of user

DELETE /api/users/:id → Remove user

💡 Improvements

Add validation logic for all fields

Use helper functions for user lookup

Return 201 for creation, 204 for deletion

⚙️ Phase 4: Middleware and Validation

Goal: Learn middleware — the backbone of Express — and add validation.

📚 What You’ll Learn

Creating custom middleware (req, res, next)

The next() function and middleware chaining

Global vs route-level middleware

Validation with express-validator and checkSchema()

🧩 Mini Project: validated-users-api

Middleware for:

Valid user ID checking

Handling 400/404 errors

Validation schema for user creation

💡 Improvements

Move validation into middlewares/validation.js

Add centralized error handler

Return structured JSON error responses

🗂️ Phase 5: Express Router & Modular Architecture

Goal: Organize growing APIs like a professional.

📚 What You’ll Learn

Express Router for modular structure

Creating route files for each module

Importing and registering routers

🧩 Mini Project: modular-api

Folder Structure:

routes/
users.mjs
products.mjs
utils/
constants.mjs
index.mjs

💡 Improvements

Add controllers/ folder for logic separation

Add logger middleware for request tracking

🍪 Phase 6: Cookies & Sessions

Goal: Handle user sessions and stateful authentication.

📚 What You’ll Learn

Setting and reading cookies

Using cookie-parser

Session management with express-session

Building session-based cart

🧩 Mini Project: auth-cart-api

Endpoints:

POST /api/auth/login → start session

GET /api/cart → return user cart

POST /api/cart/add → add items

💡 Improvements

Add session expiry logic

Middleware for authentication protection

🔐 Phase 7: Authentication, Database & Security

Goal: Build production-ready secure APIs.

📚 What You’ll Learn

Passport.js (Local + OAuth)

Mongoose for MongoDB

Password hashing with bcrypt

Persistent sessions using connect-mongo

🧩 Mini Project: secure-auth-api

Local login/register with Passport Local Strategy

Password hashing and verification

MongoDB persistence

Session store in MongoDB

💡 Improvements

Add role-based access control

OAuth login with Google/Discord

Secure all endpoints with middleware

🧪 Phase 8: Testing (Unit, Integration, E2E)

Goal: Ensure your API is robust, reliable, and production-ready.

📚 What You’ll Learn

Unit testing with Jest

Integration testing with Supertest

Mocking dependencies

Test database setup

🧩 Mini Project: tested-api

Tests:

Unit tests for handlers

Integration tests for POST /api/users

E2E tests: login → add to cart → get cart

💡 Improvements

Generate coverage reports

Automate tests with GitHub Actions

Add .env.test environment

🎓 Final Phase: Capstone Project — NodeVault API

Combine everything you’ve learned into one professional-grade API.

🏗️ Features

✅ Authentication (Passport + Sessions)
✅ MongoDB + Mongoose persistence
✅ Validation & Error Handling
✅ Modular Routers & Controllers
✅ Password Hashing + Cookies
✅ Unit & Integration Testing
✅ Optional: OAuth Login

🌟 Conclusion

You now have a roadmap to go from zero to backend hero.
Each phase builds directly on the last — by the end, you’ll have:

A complete backend portfolio project

Real-world Express.js experience

A professional-level understanding of Node.js APIs
**

Phase 1:

**
🧱 Phase 1: Project Setup and HTTP Fundamentals (Full Beginner Explanation)
🌍 What Are We Learning in This Phase?

By the end of this phase, you’ll know:

What Node.js and Express.js are.

How to set up a backend project from scratch.

How web servers actually work (clients, requests, and responses).

What each core Express function (app.get, app.listen, etc.) does.

How to build and run your first server.

🧠 Step 1: Understanding Node.js
What it is:

Node.js is a runtime environment that allows you to execute JavaScript code outside the browser.

Normally, JavaScript only runs in browsers (like Chrome or Firefox).
But Node.js brings that power to your computer — letting you build servers, APIs, and tools using JavaScript.

Think of it like this:

🧠 Browser JS → Makes buttons click, forms submit
⚙️ Node.js JS → Handles server logic, databases, file storage

So, Node.js = JavaScript for the backend.

🚂 Step 2: Understanding Express.js

Express.js is a framework built on top of Node.js.

What is a framework?

A framework gives you ready-made tools to make complex things easier.

Without Express, to build a server, you’d have to manually write:

Request parsing

Route handling

Response formatting

Express simplifies all of that.

Analogy:

Without Express = You build a car from scratch (engine, wheels, doors).
With Express = You just design the dashboard — the engine’s already there.

⚙️ Step 3: Setting Up the Project
What you do in the terminal:
mkdir express-basics-api
cd express-basics-api
npm init -y

🧩 Explanation:

mkdir → Makes a new folder

cd → Enters that folder

npm init -y → Initializes a Node project and creates a package.json file

📦 Step 4: Installing Dependencies
Install Express:
npm install express

This downloads Express.js and saves it as a dependency in your package.json.

Install Nodemon (developer tool):
npm install --save-dev nodemon

Nodemon automatically restarts your app when you change code — super useful for development.

⚙️ Step 5: Configuring package.json

Open package.json, find the "scripts" section, and replace it with:

"scripts": {
"start": "node index.mjs",
"start:dev": "nodemon index.mjs"
}

🧩 Explanation:

"start" → runs your app normally.

"start:dev" → runs your app using nodemon (auto restart).

You’ll run your app using:

npm run start:dev

💡 Step 6: Enabling Modern JavaScript (ES Modules)

By default, Node.js uses an older system (require and module.exports),
but we want to use the modern import/export syntax.

To enable that, add this to your package.json:

"type": "module"

Now you can use:

import express from "express";

instead of:

const express = require("express");

📁 Step 7: Create Entry File

Create your main file:

touch index.mjs

🧩 Step 8: Core Express Concepts Explained

Now let’s slowly build the server and explain every single line.

🔹 1. Importing Express
import express from "express";

🧩 What this means:
You are importing the Express library that you installed earlier.
This gives you access to all of Express’s features.

It’s like saying:

“Hey Express, I want to use your toolkit to build my server.”

🔹 2. Creating an Express Application
const app = express();

🧩 Explanation:
This creates an application object, often called app.

Think of app as your entire web server — it will handle:

Routes (/users, /products, etc.)

Middleware (authentication, validation)

Responses (sending data back to users)

Every route, middleware, and response you define will go through app.

🔹 3. Setting a Port
const PORT = process.env.PORT || 3000;

🧩 Explanation:
The port is like the door number your server listens on.

process.env.PORT → checks if there’s a port number from the environment (useful in production).

|| 3000 → if no port is defined, use port 3000 by default.

🔹 4. Handling Routes (app.get)
app.get("/", (req, res) => {
res.send("🚀 Server is running...");
});

🧩 Breakdown:

app.get() → defines a route that responds to GET requests (used to fetch data).

"/" → the path. / means the root URL (like homepage).

(req, res) → two parameters Express provides:

req = request object (contains info about what client sent)

res = response object (you use this to reply)

res.send() → sends plain text or HTML as response.

🔹 5. Sending JSON Responses
app.get("/api/info", (req, res) => {
res.json({
app: "Express Basics API",
version: "1.0.0",
author: "Ankit Silwal",
time: new Date().toISOString()
});
});

🧩 Explanation:

res.json() → sends back JSON data.
Express automatically sets the Content-Type: application/json header.

In real APIs, this is how servers return data (like user info, products, etc.).

🔹 6. Starting the Server (app.listen)
app.listen(PORT, () => {
console.log(✅ Server started on http://localhost:${PORT});
});

🧩 Explanation:

app.listen() → tells the server to start listening for requests on the given port.

When someone visits http://localhost:3000, the server will respond using the routes you defined.

The callback function runs once the server starts — it’s used just for logging.

✅ Your Full Code:
import express from "express";

const app = express();
const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => {
res.send("🚀 Server is running...");
});

app.get("/api/info", (req, res) => {
res.json({
app: "Express Basics API",
version: "1.0.0",
author: "Ankit Silwal",
time: new Date().toISOString()
});
});

app.listen(PORT, () => {
console.log(✅ Server started on http://localhost:${PORT});
});

Run this using:

npm run start:dev

Then open:
👉 http://localhost:3000/ → see text
👉 http://localhost:3000/api/info → see JSON

🧪 Step 9: Understanding How It All Connects

Here’s how your code works in flow:

Browser sends request to http://localhost:3000/api/info

Express (app) checks if a route matches (/api/info)

Your route handler runs (req, res)

You send back JSON using res.json()

Browser displays the response

That’s the heart of every web API.

✅ Phase 1 To-Do List

Now that you understand all concepts, here’s your Phase 1 Project Checklist:

🧩 Setup:

Initialize project with npm init -y

Install express and nodemon

Add "type": "module" to package.json

Add "start" and "start:dev" scripts

⚙️ Code:

Import Express (import express from "express")

Create app using express()

Define PORT variable (default 3000)

Create two routes:

/ → return a text message

/api/info → return JSON info

Start server with app.listen(PORT)

🔧 Improvements (Mini Challenges):

Add /health route returning:

{
"status": "OK",
"uptime": "1234 seconds",
"timestamp": "2025-11-11T10:00:00Z"
}

Use environment variables (dotenv)

Create a separate file for routes later (optional)

🎯 Goal by end of Phase 1:
You should be able to explain these terms clearly:

Node.js

Express.js

app.get()

req and res

res.send() vs res.json()

app.listen()

What is a port and how the server listens

import express from 'express';
import dotenv from 'dotenv';
dotenv.config();

const app=express();
const PORT=process.env.PORT;
app.get("/",(req,res)=>{
  res.send("This is just a project for me")
})
app.get("/api/info",(req,res)=>{
  res.json({
    author:"Ankit",
    project:"Anonymous even for me",
    platformused:"ChatGPT"
  })
})
app.get("/api/health",(req,res)=>{
  res.json({
    status:"ok",
    runtime:"from 6:22"
  })
})
app.listen(PORT,()=>{
  console.log(`The server has begun at the port number ${PORT}`);
})
Enter fullscreen mode Exit fullscreen mode

Phase 2

🧱 Phase 2: Basic Routing and Data Retrieval (GET)

🎯 Goal for This Phase
By the end of this phase, you’ll be able to:

Define multiple GET routes for different resources.

Use route parameters (/api/users/:id) to get specific items.

Use query strings (/api/users?filter=name&value=ankit) to filter results.

Send responses in both plain text and JSON format.

Understand and properly set HTTP status codes (200, 400, 404).

🧠 Step 1: Understanding the GET Method
💬 What is GET?
GET is an HTTP method used to retrieve data from the server.
It’s the most common and simplest type of request.
When you type a URL in your browser (like https://github.com),
you’re sending a GET request.
⚙️ In Express:
app.get("/api/users", (req, res) => {
res.send("List of users");
});

This means:

When a client makes a GET request to /api/users,
respond with “List of users.”

🧩 Step 2: Create Mock Data
Let’s simulate a database using an array (since we don’t have a DB yet):
const users = [
{ id: 1, name: "Ankit", age: 20, city: "Kathmandu" },
{ id: 2, name: "Ravi", age: 22, city: "Delhi" },
{ id: 3, name: "Priya", age: 19, city: "Mumbai" },
{ id: 4, name: "Rahul", age: 23, city: "Bangalore" }
];

⚙️ Step 3: Define a Base Route
The root route is just to confirm your server works.
app.get("/", (req, res) => {
res.send("🚀 Express API is running successfully!");
});

⚙️ Step 4: Create a Route to Get All Users
This returns all users in your mock array.
app.get("/api/users", (req, res) => {
res.status(200).json(users);
});

🧩 Explanation:

res.status(200) → 200 = OK (successful request)

.json(users) → sends data as JSON

Now if you visit
http://localhost:3000/api/users
you’ll get the entire array.

⚙️ Step 5: Dynamic Routing Using Route Parameters (req.params)
This allows you to get a specific user by ID, using the concept we discussed earlier.
app.get("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id); // Get the ID from the URL
if (isNaN(id)) {
return res.status(400).json({ error: "Invalid ID format" });
}

const user = users.find(u => u.id === id);

if (!user) {
return res.status(404).json({ error: "User not found" });
}

res.json(user);
});

🧠 What happens:

When you go to /api/users/2
→ Express matches :id → extracts 2
→ Finds the user with id: 2
→ Returns it as JSON

⚙️ Step 6: Using Query Strings for Filtering (req.query)
Now suppose you want to filter users by their name or city:
/api/users?filter=city&value=Delhi

Code:
app.get("/api/users", (req, res) => {
const { filter, value } = req.query;

if (filter && value) {
const filteredUsers = users.filter(user => {
return user[filter]?.toString().toLowerCase().includes(value.toLowerCase());
});

if (filteredUsers.length === 0) {
  return res.status(404).json({ error: "No users match the filter criteria" });
}

return res.json(filteredUsers);
Enter fullscreen mode Exit fullscreen mode

}

res.json(users);
});

🧩 Explanation:

req.query → captures query string values from the URL.

For /api/users?filter=city&value=Delhi:

req.query.filter → "city"

req.query.value → "Delhi"

If no filter is provided, it just returns all users.

⚙️ Step 7: Send Proper HTTP Status Codes
Common status codes to use:
CodeMeaningWhen to Use200OKSuccessful request201CreatedWhen something new is created (used later)400Bad RequestWhen user input is invalid404Not FoundResource doesn’t exist500Server ErrorSomething went wrong on the backend
In this phase, you’ll mostly use 200, 400, and 404.

💻 Full Example Code for Phase 2
Here’s how your file might look by now:
import express from "express";
import dotenv from "dotenv";

dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;

const users = [
{ id: 1, name: "Ankit", age: 20, city: "Kathmandu" },
{ id: 2, name: "Ravi", age: 22, city: "Delhi" },
{ id: 3, name: "Priya", age: 19, city: "Mumbai" },
{ id: 4, name: "Rahul", age: 23, city: "Bangalore" }
];

// Base route
app.get("/", (req, res) => {
res.send("🚀 Express API is running successfully!");
});

// Get all users or filtered users
app.get("/api/users", (req, res) => {
const { filter, value } = req.query;

if (filter && value) {
const filteredUsers = users.filter(user =>
user[filter]?.toString().toLowerCase().includes(value.toLowerCase())
);

if (filteredUsers.length === 0) {
  return res.status(404).json({ error: "No users found" });
}

return res.status(200).json(filteredUsers);
Enter fullscreen mode Exit fullscreen mode

}

res.status(200).json(users);
});

// Get a specific user by ID
app.get("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);

if (isNaN(id)) {
return res.status(400).json({ error: "Invalid ID format" });
}

const user = users.find(u => u.id === id);

if (!user) {
return res.status(404).json({ error: "User not found" });
}

res.status(200).json(user);
});

app.listen(PORT, () => {
console.log(✅ Server running at http://localhost:${PORT});
});

🔧 Phase 2 To-Do List (Practice Tasks)
🧩 Setup:

Add a mock array of users to your app.

Define a route /api/users that returns all users.

Define a route /api/users/:id that returns a specific user by ID.

Handle invalid/nonexistent IDs with proper HTTP status codes.

Add filtering logic with query strings:

/api/users?filter=city&value=Delhi

/api/users?filter=name&value=ankit

💡 Improvement Tasks:

Add a /api/products route with its own array and parameters.

Write a helper function validateId(id) that checks if ID is valid.

Add custom error messages with status and message fields.

Try combining query + parameter (e.g., /api/users/:id?detail=full).

✅ Learning Outcome
After finishing Phase 2, you will clearly understand:

How GET requests work.

The difference between static routes, route parameters, and query strings.

How Express handles URL data using req.params and req.query.

How to send dynamic responses and handle errors gracefully.

Next, we’ll move to Phase 3: Creating and Modifying Data (POST, PUT, PATCH, DELETE) — that’s when you’ll learn how APIs actually accept and modify data, making your server truly dynamic.
Would you like me to start Phase 3: CRUD Operations (POST, PUT, PATCH, DELETE) next?

import express from 'express';
import dotenv from 'dotenv';
dotenv.config();
const users=[{
    id:1,
    name:"Ankit",
    systemid:"2024900700",
    course:"Btech"
  },{
    id:2,
    name:"Nandalal",
    systemid:"202489484",
    course:"Engineering"
  },{
    id:3,
    name:"Anish",
    systemid:"2024948554",
    course:"Bpharma"
  }]
const app=express();
const PORT=process.env.PORT;
app.get("/",(req,res)=>{
  res.send("Just gettng the required data")
})
app.get("/api/users", (req, res) => {
  const { filter, value } = req.query;
  if (filter && value) {
    const filteredUsers = users.filter(user => {
      return user[filter]?.toString().toLowerCase().includes(value.toLowerCase());
    });
    if (filteredUsers.length === 0) {
      return res.status(404).json({ error: "No users match the filter criteria" });
    }
    return res.json(filteredUsers);
  }
  res.json(users);
});
app.get('/api/users/:id',(req,res)=>{
  const query=req.query;
  const id=parseInt(req.params.id);
  const user=users.find(usr=>usr.id===id);
  if(!user){
    res.send("The user wasnt found!")
  }
  if(!query.details){
    res.json(user)  
  }
  if(query.details=="name"){
    res.send(user.name);
  }
  if(query.details=="systemid"){
    res.send(user.systemid);
  }
  res.json(user);
})
app.get("/api/health",(req,res)=>{
  res.json({
    status:"ok",
    runtime:"from 6:22"
  })
})
app.listen(PORT,()=>{
  console.log(`The server has begun at the port number ${PORT}`);
}) 
Enter fullscreen mode Exit fullscreen mode

Phase 3

🧱 Phase 3: Creating and Modifying Data (POST, PUT, PATCH, DELETE)
🎯 Goal of This Phase

By the end, you’ll be able to:

Create new data using POST requests.

Update existing data using PUT and PATCH.

Delete data using DELETE.

Parse incoming JSON request bodies using Express middleware.

Understand and apply proper HTTP status codes for each operation.

🧠 Step 1: What is CRUD?

CRUD = Create, Read, Update, Delete
It’s the core of every backend system.

Operation HTTP Method Description Example
Create POST Add new data /api/users
Read GET Fetch data /api/users or /api/users/:id
Update PUT or PATCH Modify existing data /api/users/:id
Delete DELETE Remove data /api/users/:id

You already covered “R” in CRUD during Phase 2.
Now, let’s learn C, U, and D.

🧩 Step 2: Setting up JSON Middleware

When clients send data to the backend (like forms or JSON payloads),
Express doesn’t automatically parse that data.

We need to enable JSON parsing middleware:

app.use(express.json());

💡 Explanation:

This built-in middleware lets Express read JSON request bodies (like when a frontend sends data via POST).

Add this line before your routes in your code:

const app = express();
app.use(express.json());

⚙️ Step 3: The POST Request — Creating Data

Let’s start by allowing users to add a new user.

Code:
app.post("/api/users", (req, res) => {
const newUser = req.body;

// Validation: ensure required fields exist
if (!newUser.name || !newUser.age || !newUser.city) {
return res.status(400).json({ error: "Missing required fields" });
}

// Generate a new ID (auto-increment style)
newUser.id = users.length ? users[users.length - 1].id + 1 : 1;

users.push(newUser);

res.status(201).json({
message: "User created successfully",
data: newUser
});
});

🧠 Explanation:

req.body contains the data the client sends.

res.status(201) = “Created” — the standard success code for new resources.

We push the new object into the users array.

Example POST body (in Thunder Client or Postman):

{
"name": "Sita",
"age": 21,
"city": "Kolkata"
}

Response:

{
"message": "User created successfully",
"data": {
"id": 5,
"name": "Sita",
"age": 21,
"city": "Kolkata"
}
}

⚙️ Step 4: The PUT Request — Full Update
Concept:

PUT completely replaces the existing data.

If any field is missing, it’s treated as removed.

Code:
app.put("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const updatedUser = req.body;

if (isNaN(id)) return res.status(400).json({ error: "Invalid ID format" });

const index = users.findIndex(u => u.id === id);
if (index === -1) return res.status(404).json({ error: "User not found" });

// Replace entire object (old method)
users[index] = { id, ...updatedUser };

res.status(200).json({
message: "User replaced successfully",
data: users[index]
});
});

🧠 Example:
Request → PUT /api/users/2
Body:

{
"name": "Ravi Updated",
"age": 23,
"city": "Pune"
}

Response:

{
"message": "User replaced successfully",
"data": { "id": 2, "name": "Ravi Updated", "age": 23, "city": "Pune" }
}

If you omit any property, the old one is erased, not kept.

⚙️ Step 5: The PATCH Request — Partial Update
Concept:

PATCH modifies only specific fields.

It merges the new data with the old one.

Code:
app.patch("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const partialUpdate = req.body;

if (isNaN(id)) return res.status(400).json({ error: "Invalid ID format" });

const user = users.find(u => u.id === id);
if (!user) return res.status(404).json({ error: "User not found" });

// Merge old and new data
Object.assign(user, partialUpdate);

res.status(200).json({
message: "User updated successfully",
data: user
});
});

🧠 Example:
Request → PATCH /api/users/2
Body:

{
"city": "Lucknow"
}

Response:

{
"message": "User updated successfully",
"data": { "id": 2, "name": "Ravi Updated", "age": 23, "city": "Lucknow" }
}

⚙️ Step 6: The DELETE Request — Removing Data
Code:
app.delete("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID format" });

const index = users.findIndex(u => u.id === id);
if (index === -1) return res.status(404).json({ error: "User not found" });

users.splice(index, 1);

res.status(204).send(); // 204 = No Content (successful deletion)
});

🧠 Explanation:

splice(index, 1) removes the user.

204 status means the request succeeded but there’s no content to return.

⚙️ Step 7: Test Your API

Use Thunder Client (or Postman) and test each route:

Operation Method Route Example Body
Create POST /api/users { "name": "Sita", "age": 21, "city": "Kolkata" }
Read All GET /api/users —
Read One GET /api/users/2 —
Update Full PUT /api/users/2 { "name": "Ravi Updated", "age": 23, "city": "Pune" }
Update Partial PATCH /api/users/2 { "city": "Lucknow" }
Delete DELETE /api/users/2 —
🧾 Full Example File (Phase 3 Ready)
import express from "express";
import dotenv from "dotenv";

dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json()); // Middleware for parsing JSON bodies

const users = [
{ id: 1, name: "Ankit", age: 20, city: "Kathmandu" },
{ id: 2, name: "Ravi", age: 22, city: "Delhi" },
{ id: 3, name: "Priya", age: 19, city: "Mumbai" },
{ id: 4, name: "Rahul", age: 23, city: "Bangalore" }
];

// CREATE (POST)
app.post("/api/users", (req, res) => {
const newUser = req.body;
if (!newUser.name || !newUser.age || !newUser.city)
return res.status(400).json({ error: "Missing required fields" });

newUser.id = users.length ? users[users.length - 1].id + 1 : 1;
users.push(newUser);
res.status(201).json({ message: "User created", data: newUser });
});

// READ (GET)
app.get("/api/users", (req, res) => res.json(users));

// READ (GET by ID)
app.get("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
});

// UPDATE (PUT)
app.put("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const index = users.findIndex(u => u.id === id);
if (index === -1) return res.status(404).json({ error: "User not found" });
users[index] = { id, ...req.body };
res.json({ message: "User replaced", data: users[index] });
});

// PARTIAL UPDATE (PATCH)
app.patch("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) return res.status(404).json({ error: "User not found" });
Object.assign(user, req.body);
res.json({ message: "User updated", data: user });
});

// DELETE
app.delete("/api/users/:id", (req, res) => {
const id = parseInt(req.params.id);
const index = users.findIndex(u => u.id === id);
if (index === -1) return res.status(404).json({ error: "User not found" });
users.splice(index, 1);
res.status(204).send();
});

app.listen(PORT, () => {
console.log(✅ Server running on http://localhost:${PORT});
});

🧩 Phase 3 To-Do Checklist
Setup:

Add app.use(express.json()).

Implement routes:

POST /api/users → create new user.

PUT /api/users/:id → replace full user.

PATCH /api/users/:id → modify part of a user.

DELETE /api/users/:id → remove user.

Validate inputs and handle 404, 400 properly.

Improvements:

Create helper function findUserIndex(id).

Return success/failure messages consistently.

Add a check to prevent duplicate names (just for practice).

Separate routes into another file later (to prepare for Phase 5).

🎯 Learning Outcome

By finishing this phase, you’ll:

Understand how data flows from client → server → memory.

Know the difference between PUT and PATCH.

Master using Express middleware for parsing data.

Be comfortable handling all four CRUD operations.

Top comments (0)