🎯 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}`);
})
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);
}
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);
}
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}`);
})
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)