Skip to main content

Command Palette

Search for a command to run...

Creating Routes and Handling Requests with Express

Updated
β€’33 min read
Creating Routes and Handling Requests with Express

1. What Express.js is

1. Introduction

The web development world runs on servers. Every time you open a website, your browser sends a request to a server, and the server sends back a response. Building these servers from scratch in pure Node.js is possible β€” but painful. This is where Express.js steps in. Express is a lightweight, fast, and flexible framework built on top of Node.js that makes building web servers and APIs clean, simple, and enjoyable. It is one of the most widely used backend frameworks in the JavaScript ecosystem today.

2. Definition

Express.js is a minimal and unopinionated web application framework for Node.js that provides a robust set of features for building web and mobile applications β€” including routing, middleware support, and HTTP utilities.

In simpler terms:

  • Node.js = the engine πŸ”§

  • Express.js = the car built on that engine

3. Explanation

Raw Node.js HTTP Server (The Hard Way)

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello, World!');
  } else if (req.url === '/about' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');
  } else {
    res.writeHead(404);
    res.end('Not Found');
  }
});

server.listen(3000);

❌ Problems:

  • Manual URL checking

  • Manual method checking

  • No structure β€” grows messy fast

Express.js Server (The Easy Way)

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.get('/about', (req, res) => {
  res.send('About Page');
});

app.listen(3000, () => console.log('Server running on port 3000'));

Benefits:

  • Clean, readable routing

  • Built-in method handling (GET, POST, etc.)

  • Easily scalable

Request β†’ Route Handler β†’ Response Flow

Client (Browser/Postman)
        β”‚
        β”‚  HTTP Request (GET /about)
        β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Express.js β”‚
  β”‚   Server    β”‚
  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β”‚  Matches route: app.get('/about')
         β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Route Handler  β”‚
  β”‚ (req, res) => { β”‚
  β”‚  res.send(...)  β”‚
  β”‚ }               β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           β”‚  HTTP Response
           β–Ό
     Client receives
       "About Page"

Express Routing Structure

app
 β”œβ”€β”€ app.get('/')          β†’ Home Page
 β”œβ”€β”€ app.get('/about')     β†’ About Page
 β”œβ”€β”€ app.post('/login')    β†’ Login Handler
 β”œβ”€β”€ app.post('/register') β†’ Register Handler
 └── app.delete('/user')   β†’ Delete User

Each route = a combination of:

Part Example
HTTP Method GET, POST, PUT, DELETE
URL Path /, /about, /user
Handler Function (req, res) => { ... }

4. Example β€” Mini Express App

const express = require('express');
const app = express();

app.use(express.json()); // Middleware to parse JSON

// GET route
app.get('/user', (req, res) => {
  res.json({ name: 'Rahul', age: 25 });
});

// POST route
app.post('/user', (req, res) => {
  const { name } = req.body;
  res.json({ message: `User ${name} created!` });
});

app.listen(3000, () => console.log('πŸš€ Server on http://localhost:3000'));

Flow:

GET  /user  β†’  returns { name: "Rahul", age: 25 }
POST /user  β†’  body: { name: "Anjali" } β†’ returns { message: "User Anjali created!" }

5. Conclusion

Express.js transforms the complex, repetitive process of building HTTP servers in raw Node.js into a clean, structured, and scalable experience. By abstracting away low-level details, Express lets developers focus on what matters most β€” the logic of their application. Its routing system is intuitive, its middleware system is powerful, and its simplicity makes it the go-to starting point for anyone learning backend development with JavaScript. Whether you are building a simple REST API or a full-scale web application, Express.js is the foundation you can trust.


2. Why Express simplifies Node.js development

1. Introduction

Node.js is powerful β€” it lets JavaScript run on the server side, handle thousands of requests, and build blazing-fast applications. But raw Node.js comes with a cost: you have to build everything yourself. Every route, every method check, every header β€” all manual. As your application grows, this becomes a tangled mess of if/else blocks and repeated code. Express.js was created to solve exactly this problem. It sits on top of Node.js and provides a clean, structured way to handle requests, define routes, and send responses β€” turning complex server code into something readable, maintainable, and elegant.

2. Definition

Express.js simplifies Node.js development by abstracting repetitive low-level HTTP logic into a clean API β€” giving developers built-in routing, middleware support, and response helpers so they can focus on application logic instead of boilerplate code.

Think of it this way:

Raw Node.js  =  Building a house brick by brick 
Express.js   =  Building a house with pre-made walls 

Both use the same foundation β€” but one is dramatically faster and cleaner.

3. Explanation

πŸ”΄ Problem β€” Raw Node.js HTTP Server

const http = require('http');

const server = http.createServer((req, res) => {

  // ❌ Manually check every URL
  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Home Page');

  } else if (req.url === '/about' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');

  } else if (req.url === '/login' && req.method === 'POST') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ message: 'Logged in' }));

  } else {
    res.writeHead(404);
    res.end('Not Found');
  }
});

server.listen(3000);

❌ What's wrong here?

Problem Impact
Manual if/else for every route Code grows uncontrollably
Manual writeHead() every time Repetitive boilerplate
No method separation Easy to make mistakes
No JSON parsing built-in Extra work for every request
No structure Impossible to scale

🟒 Solution β€” Express.js Server

const express = require('express');
const app = express();

app.use(express.json()); //  JSON parsing in ONE line

//  Clean, separated routes
app.get('/', (req, res) => {
  res.send('Home Page');
});

app.get('/about', (req, res) => {
  res.send('About Page');
});

app.post('/login', (req, res) => {
  res.json({ message: 'Logged in' });
});

app.listen(3000, () => console.log('πŸš€ Running on port 3000'));

What Express fixed?

Raw Node.js Express.js
if (req.url === '/' && req.method === 'GET') app.get('/')
res.writeHead(200, {'Content-Type': ...}) res.send() / res.json()
Manual JSON parsing app.use(express.json())
Everything in one block Separate, clean route handlers
Hard to scale Modular and scalable

πŸ” Request β†’ Route Handler β†’ Response Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚              CLIENT (Browser / Postman)          β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β”‚  HTTP Request
                         β”‚  GET /about
                         β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                  EXPRESS SERVER                  β”‚
 β”‚                                                  β”‚
 β”‚   app.get('/')       β†’ skipped βœ—                 β”‚
 β”‚   app.get('/about')  β†’ MATCHED βœ“                 β”‚
 β”‚   app.post('/login') β†’ skipped βœ—                 β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚              ROUTE HANDLER                       β”‚
 β”‚                                                  β”‚
 β”‚   app.get('/about', (req, res) => {              β”‚
 β”‚       res.send('About Page');   ← executes       β”‚
 β”‚   });                                            β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β”‚  HTTP Response
                         β”‚  "About Page"
                         β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚              CLIENT receives response            β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ—ΊοΈ Express Routing Structure Visualization

EXPRESS APP
β”‚
β”œβ”€β”€ Middleware Layer
β”‚   β”œβ”€β”€ express.json()        β†’ parses JSON body
β”‚   β”œβ”€β”€ express.urlencoded()  β†’ parses form data
β”‚   └── custom middleware()   β†’ logging, auth, etc.
β”‚
β”œβ”€β”€ Routes
β”‚   β”œβ”€β”€ GET    /              β†’ Home handler
β”‚   β”œβ”€β”€ GET    /about         β†’ About handler
β”‚   β”œβ”€β”€ POST   /login         β†’ Login handler
β”‚   β”œβ”€β”€ POST   /register      β†’ Register handler
β”‚   β”œβ”€β”€ PUT    /user/:id      β†’ Update user handler
β”‚   └── DELETE /user/:id      β†’ Delete user handler
β”‚
└── Error Handler
    └── app.use((err, req, res, next) => { ... })

Every request flows top to bottom through this structure until a matching route is found.

4. Example β€” Side by Side Minimal App

Same app. Two ways. See the difference.

// ==============================
// ❌  RAW NODE.JS  (15+ lines of chaos)
// ==============================
const http = require('http');
http.createServer((req, res) => {
  if (req.url === '/user' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ name: 'Rahul' }));
  } else {
    res.writeHead(404);
    res.end('Not Found');
  }
}).listen(3000);


// ==============================
// βœ…  EXPRESS.JS  (Clean & simple)
// ==============================
const express = require('express');
const app = express();

app.get('/user', (req, res) => {
  res.json({ name: 'Rahul' }); // βœ… One clean line
});

app.listen(3000);

Result is identical. But Express code is:

  • Half the lines

  • Easier to read

  • Easier to extend

  • Easier to debug


5. Conclusion

Express.js doesn't add magic β€” it adds clarity. It takes everything that is painful about raw Node.js HTTP servers β€” manual routing, repetitive headers, unstructured code β€” and replaces it with a simple, consistent, and scalable pattern. By separating routes, automating boilerplate, and providing clean helper methods like res.json() and res.send(), Express lets developers write less code and build more features. For anyone working with Node.js, learning Express is not just recommended β€” it is essential.


3. Creating first Express server

1. Introduction

Every backend journey begins with one milestone β€” getting your first server running. In raw Node.js, this feels overwhelming. In Express.js, it feels like a superpower. With just a few lines of code, you can have a fully working web server that listens for requests, matches routes, and sends back responses. Creating your first Express server is not just a technical step β€” it is the moment everything clicks. You understand how the internet works, how data flows, and how your code connects to the outside world. This section walks you through that moment, step by step.

2. Definition

Creating an Express server means installing Express, initializing an app instance, defining at least one route, and telling the server to listen on a port β€” so it can receive and respond to HTTP requests from clients.

The 4 building blocks of every Express server:

1. require('express')      β†’  Import Express
2. express()               β†’  Create the app
3. app.get() / app.post()  β†’  Define routes
4. app.listen()            β†’  Start the server

3. Explanation

Step 1 β€” Project Setup

Before writing any code, set up your Node project:

# Create a new project folder
mkdir my-express-app
cd my-express-app

# Initialize Node project (creates package.json)
npm init -y

# Install Express
npm install express

Your folder now looks like:

my-express-app/
 β”œβ”€β”€ node_modules/      ← Express lives here
 β”œβ”€β”€ package.json       ← Project config
 └── index.js           ← Your server file (create this)

Step 2 β€” Writing the Server

// index.js

// Step 1: Import Express
const express = require('express');

// Step 2: Create the app instance
const app = express();

// Step 3: Define the port
const PORT = 3000;

// Step 4: Create your first route
app.get('/', (req, res) => {
  res.send('Hello World! My first Express server is running πŸš€');
});

// Step 5: Start the server
app.listen(PORT, () => {
  console.log(`βœ… Server is running on http://localhost:${PORT}`);
});

Line-by-Line Breakdown

const express = require('express');

↳ Loads the Express package from node_modules

const app = express();

↳ Creates your Express application β€” this IS your server

app.get('/', (req, res) => { ... })

↳ Listens for GET requests on the '/' (home) route

res.send('Hello World!')

↳ Sends a plain text response back to the client

app.listen(3000, () => { ... })

↳ Starts the server and opens port 3000 for traffic

Request β†’ Route Handler β†’ Response Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚            CLIENT                               β”‚
 β”‚   Browser visits: http://localhost:3000/        β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β”‚  GET /
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚            EXPRESS APP                          β”‚
 β”‚                                                 β”‚
 β”‚   const app = express()                         β”‚
 β”‚   app.listen(3000) ← server is awake here       β”‚
 β”‚                                                 β”‚
 β”‚   Incoming request: GET /                       β”‚
 β”‚   Scanning routes...                            β”‚
 β”‚                                                 β”‚
 β”‚   app.get('/')  βœ“  MATCHED!                     β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚            ROUTE HANDLER                        β”‚
 β”‚                                                 β”‚
 β”‚   (req, res) => {                               β”‚
 β”‚       res.send('Hello World! πŸš€')  ← runs       β”‚
 β”‚   }                                             β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β”‚  HTTP Response
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   CLIENT receives β†’ "Hello World! πŸš€"           β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Express Routing Structure β€” First Server

EXPRESS APP (app)
β”‚
β”œβ”€β”€ PORT: 3000
β”‚   └── app.listen(3000) ← server starts here
β”‚
β”œβ”€β”€ GET  /
β”‚   └── res.send('Hello World! πŸš€')
β”‚
β”œβ”€β”€ GET  /about
β”‚   └── res.send('About Page')
β”‚
β”œβ”€β”€ GET  /contact
β”‚   └── res.send('Contact Page')
β”‚
└── [No match found]
    └── Express returns 404 automatically

Step 3 β€” Adding More Routes

const express = require('express');
const app = express();
const PORT = 3000;

// Home route
app.get('/', (req, res) => {
  res.send('🏠 Home Page');
});

// About route
app.get('/about', (req, res) => {
  res.send('πŸ“– About Page');
});

// Contact route
app.get('/contact', (req, res) => {
  res.send('πŸ“ž Contact Page');
});

// JSON response route
app.get('/user', (req, res) => {
  res.json({
    name: 'Rahul',
    age: 25,
    role: 'Developer'
  });
});

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

Step 4 β€” Running the Server

node index.js

Terminal output:

Server running at http://localhost:3000

Now open your browser and visit http://localhost:3000/ β€” your server is live.

Pro Tip: Install nodemon so the server auto-restarts on file changes:

npm install -g nodemon
nodemon index.js

4. Example β€” Complete First Server (Clean & Minimal)

// index.js β€” Complete First Express Server

const express = require('express');
const app = express();

app.use(express.json()); // Enable JSON body parsing

// ─── ROUTES ───────────────────────────────────────

app.get('/', (req, res) => {
  res.send('πŸš€ Server is up and running!');
});

app.get('/user', (req, res) => {
  res.json({ id: 1, name: 'Rahul', role: 'Developer' });
});

app.post('/user', (req, res) => {
  const { name } = req.body;
  res.json({ message: `βœ… User "${name}" has been created!` });
});

// ─── START SERVER ─────────────────────────────────

app.listen(3000, () => {
  console.log('βœ… Server running on http://localhost:3000');
});

Test it:

GET  /        β†’  "πŸš€ Server is up and running!"
GET  /user    β†’  { id: 1, name: "Rahul", role: "Developer" }
POST /user    β†’  body: { "name": "Priya" }
              β†’  { message: "βœ… User \"Priya\" has been created!" }

5. Conclusion

Creating your first Express server is one of the most satisfying moments in backend development. In under 15 lines of code, you have a real, working server that can receive requests, match routes, and send meaningful responses. What would take 50+ messy lines in raw Node.js becomes a clean, readable, and expandable structure in Express. This is just the beginning β€” from here, you can add databases, authentication, file uploads, and much more. But every complex Express application, no matter how large, starts exactly like this.


4. Handling GET requests

1. Introduction

Every time you type a URL into your browser and hit enter, you are sending a GET request. It is the most common, most fundamental HTTP method in the web. GET requests are how clients ask for data β€” a webpage, a list of users, a product details page, a JSON response from an API. Understanding how to handle GET requests in Express.js is the very core of backend development. Raw Node.js makes this tedious and error-prone. Express makes it clean, intuitive, and powerful β€” with support for static routes, dynamic parameters, and query strings all in one simple pattern.

2. Definition

A GET request is an HTTP method used by a client to retrieve data from a server. In Express.js, GET requests are handled using app.get(path, handler) β€” where the path defines the URL and the handler defines what data to send back.

GET Request β€” Key Rules:

βœ… Used to FETCH / READ data
βœ… Data sent via URL (not request body)
βœ… Safe β€” does not change server data
βœ… Cacheable by browsers
❌ Should NOT be used to send passwords or sensitive data

The Three Types of GET Requests in Express:

1. Static Route      β†’  /users
2. Dynamic Route     β†’  /users/:id
3. Query String      β†’  /users?age=25&city=Delhi

3. Explanation

πŸ”΄ Handling GET in Raw Node.js (The Hard Way)

const http = require('http');

http.createServer((req, res) => {

  // ❌ Manually parse URL
  const url = req.url;
  const method = req.method;

  if (url === '/users' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify([{ id: 1, name: 'Rahul' }]));

  } else if (url.startsWith('/users/') && method === 'GET') {
    // ❌ Manually extract ID from URL
    const id = url.split('/')[2];
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ id, name: 'Rahul' }));

  } else {
    res.writeHead(404);
    res.end('Not Found');
  }

}).listen(3000);

Type 1 β€” Static GET Route in Express

const express = require('express');
const app = express();

// βœ… Clean static route
app.get('/users', (req, res) => {
  res.json([
    { id: 1, name: 'Rahul' },
    { id: 2, name: 'Priya' },
    { id: 3, name: 'Anjali' }
  ]);
});

app.listen(3000);
GET /users  β†’  [ { id:1, name:"Rahul" }, { id:2, name:"Priya" }, ... ]

Type 2 β€” Dynamic Route Parameters (:id)

// βœ… :id is a dynamic segment β€” changes per request
app.get('/users/:id', (req, res) => {

  const id = req.params.id; // ← Express extracts it automatically

  res.json({ id, name: 'Rahul', role: 'Developer' });
});
GET /users/1   β†’  { id: "1", name: "Rahul", role: "Developer" }
GET /users/42  β†’  { id: "42", name: "Rahul", role: "Developer" }
GET /users/99  β†’  { id: "99", name: "Rahul", role: "Developer" }

req.params holds all dynamic URL segments automatically.

Type 3 β€” Query Strings (?key=value)

// URL: /search?name=Rahul&city=Delhi
app.get('/search', (req, res) => {

  const name = req.query.name; // ← "Rahul"
  const city = req.query.city; // ← "Delhi"

  res.json({
    message: `Searching for \({name} in \){city}`
  });
});
GET /search?name=Rahul&city=Delhi
β†’  { message: "Searching for Rahul in Delhi" }

GET /search?name=Priya&city=Mumbai
β†’  { message: "Searching for Priya in Mumbai" }

Request β†’ Route Handler β†’ Response Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                    CLIENT                            β”‚
 β”‚         GET /users/5?format=json                     β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β”‚  HTTP GET Request
                           β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                 EXPRESS SERVER                       β”‚
 β”‚                                                      β”‚
 β”‚   app.get('/users')       βœ—  no match                β”‚
 β”‚   app.get('/users/:id')   βœ“  MATCHED!                β”‚
 β”‚                                                      β”‚
 β”‚   Express extracts:                                  β”‚
 β”‚   β†’ req.params.id  =  "5"                            β”‚
 β”‚   β†’ req.query.format = "json"                        β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                 ROUTE HANDLER                        β”‚
 β”‚                                                      β”‚
 β”‚   app.get('/users/:id', (req, res) => {              β”‚
 β”‚       const id     = req.params.id;   // "5"         β”‚
 β”‚       const format = req.query.format;// "json"      β”‚
 β”‚       res.json({ id, name: 'Rahul' }); ← executes   β”‚
 β”‚   });                                                β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β”‚  HTTP Response
                           β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   CLIENT receives β†’  { id: "5", name: "Rahul" }      β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Express GET Routing Structure Visualization

EXPRESS APP
β”‚
β”œβ”€β”€ GET  /                        β†’ Home page
β”‚
β”œβ”€β”€ GET  /users                   β†’ All users list
β”‚   └── res.json([...users])
β”‚
β”œβ”€β”€ GET  /users/:id               β†’ Single user by ID
β”‚   β”œβ”€β”€ req.params.id  ← from URL path
β”‚   └── res.json({ id, name })
β”‚
β”œβ”€β”€ GET  /search                  β†’ Search with filters
β”‚   β”œβ”€β”€ req.query.name ← from URL ?name=...
β”‚   β”œβ”€β”€ req.query.city ← from URL ?city=...
β”‚   └── res.json({ results })
β”‚
└── GET  /users/:id/posts/:postId β†’ Nested dynamic route
    β”œβ”€β”€ req.params.id
    β”œβ”€β”€ req.params.postId
    └── res.json({ user, post })

Where Data Lives in a GET Request

URL:  GET /users/5?format=json&page=2
            β”‚  β”‚         β”‚         β”‚
            β”‚  β”‚         β”‚         └── req.query.page   = "2"
            β”‚  β”‚         └──────────── req.query.format = "json"
            β”‚  └────────────────────── req.params.id    = "5"
            └───────────────────────── matched route: /users/:id

4. Example β€” Complete GET Request Handler

// index.js β€” Handling All Types of GET Requests

const express = require('express');
const app = express();

// ─── Fake Database ────────────────────────────────
const users = [
  { id: 1, name: 'Rahul',  city: 'Delhi',   age: 25 },
  { id: 2, name: 'Priya',  city: 'Mumbai',  age: 28 },
  { id: 3, name: 'Anjali', city: 'Delhi',   age: 22 },
  { id: 4, name: 'Karan',  city: 'Chennai', age: 30 },
];

// ─── ROUTE 1: Get all users ───────────────────────
app.get('/users', (req, res) => {
  res.json(users);
});

// ─── ROUTE 2: Get user by ID ──────────────────────
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));

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

  res.json(user);
});

// ─── ROUTE 3: Search users by city ───────────────
app.get('/search', (req, res) => {
  const { city } = req.query;

  const results = users.filter(u =>
    u.city.toLowerCase() === city?.toLowerCase()
  );

  res.json({ city, count: results.length, results });
});

// ─── START SERVER ─────────────────────────────────
app.listen(3000, () => {
  console.log('βœ… Server running on http://localhost:3000');
});

Test Results:

GET /users
β†’ [ { id:1, name:"Rahul" }, { id:2, name:"Priya" }, ... ]

GET /users/2
β†’ { id:2, name:"Priya", city:"Mumbai", age:28 }

GET /users/99
β†’ { error: "User not found" }

GET /search?city=Delhi
β†’ { city:"Delhi", count:2, results:[Rahul, Anjali] }

5. Conclusion

GET requests are the heartbeat of the web β€” every page load, every data fetch, every API call starts with one. Express.js makes handling them effortless by providing three powerful tools: static routes for fixed paths, route parameters for dynamic data, and query strings for flexible filtering. What once required messy URL parsing and manual string splitting in raw Node.js is now handled automatically through req.params and req.query. Mastering GET request handling in Express means you can build any read operation your application needs β€” cleanly, confidently, and at scale.


5. Handling POST requests

1. Introduction

While GET requests ask for data, POST requests send data. Every time you fill out a form, create an account, submit a login, or place an order β€” a POST request is fired. It carries data inside its body, travels to the server, and expects something meaningful back. But a POST request is only half the story. What the server sends back β€” the response β€” is equally important. The response tells the client what happened: success, failure, created, forbidden, not found. Together, POST requests and well-structured responses form the backbone of every real-world web application. Express makes both clean, readable, and powerful.


2. Definition

A POST request is an HTTP method used to send data to the server β€” typically to create a new resource. In Express, it is handled with app.post(path, handler), and the sent data is accessed via req.body.

Sending a response means the server communicates back to the client using methods like res.send(), res.json(), or res.status() β€” with a body, a status code, and optionally headers.

POST vs GET β€” Core Difference:

GET  β†’  Data travels in the URL        β†’  /search?name=Rahul
POST β†’  Data travels in the BODY       β†’  { "name": "Rahul" }

GET  β†’  Fetches / Reads data           β†’  Safe, Cacheable
POST β†’  Creates / Submits data         β†’  Not cached

Response Anatomy:

HTTP Response
β”‚
β”œβ”€β”€ Status Code  β†’  200, 201, 400, 404, 500
β”œβ”€β”€ Headers      β†’  Content-Type, Authorization
└── Body         β†’  JSON, text, HTML

3. Explanation

Handling POST in Raw Node.js (The Hard Way)

const http = require('http');

http.createServer((req, res) => {

  if (req.url === '/users' && req.method === 'POST') {

    let body = '';

    // ❌ Manually collect chunks of data
    req.on('data', chunk => {
      body += chunk.toString();
    });

    // ❌ Manually parse JSON after stream ends
    req.on('end', () => {
      const data = JSON.parse(body);

      // ❌ Manually set every header
      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({
        message: 'User created',
        user: data
      }));
    });

  } else {
    res.writeHead(404);
    res.end('Not Found');
  }

}).listen(3000);

Handling POST in Express (The Clean Way)

const express = require('express');
const app = express();

// βœ… One line replaces ALL manual body parsing
app.use(express.json());

app.post('/users', (req, res) => {

  const { name, email } = req.body; // βœ… Data auto-parsed

  res.status(201).json({            // βœ… Status + JSON in one line
    message: 'User created!',
    user: { name, email }
  });

});

app.listen(3000);

Understanding req.body

// Client sends this POST request:
// POST /users
// Body: { "name": "Rahul", "email": "rahul@gmail.com", "age": 25 }

app.post('/users', (req, res) => {

  console.log(req.body);
  // Output: { name: "Rahul", email: "rahul@gmail.com", age: 25 }

  const { name, email, age } = req.body; // ← destructure cleanly

  res.status(201).json({ name, email, age });
});

Important: Without app.use(express.json()), req.body will be undefined.

// βœ… Always add this BEFORE your routes
app.use(express.json());

Sending Responses β€” All Methods

res.send() β€” Plain text or HTML
app.get('/', (req, res) => {
  res.send('Hello World!');           // plain text
  res.send('<h1>Hello World!</h1>');  // HTML
});

res.json() β€” JSON response

app.get('/user', (req, res) => {
  res.json({ id: 1, name: 'Rahul' }); // βœ… auto sets Content-Type
});
res.status() β€” Set HTTP status code
app.get('/user/:id', (req, res) => {
  const found = false;

  if (!found) {
    return res.status(404).json({ error: 'User not found' });
    //          ↑ chainable with .json() or .send()
  }

  res.status(200).json({ id: 1, name: 'Rahul' });
});
res.status() β€” Common Status Codes
2xx  β†’  Success
 β”œβ”€β”€ 200  OK               β†’  Request succeeded
 β”œβ”€β”€ 201  Created          β†’  Resource created (POST)
 └── 204  No Content       β†’  Success, nothing to return (DELETE)

4xx  β†’  Client Error
 β”œβ”€β”€ 400  Bad Request      β†’  Invalid data sent
 β”œβ”€β”€ 401  Unauthorized     β†’  Login required
 β”œβ”€β”€ 403  Forbidden        β†’  No permission
 └── 404  Not Found        β†’  Route doesn't exist

5xx  β†’  Server Error
 └── 500  Internal Error   β†’  Something broke on server

POST Request β†’ Route Handler β†’ Response Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                      CLIENT                          β”‚
 β”‚                                                      β”‚
 β”‚   POST /users                                        β”‚
 β”‚   Body: { "name": "Rahul", "email": "r@gmail.com" }  β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β”‚  HTTP POST Request + Body
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                 EXPRESS MIDDLEWARE                   β”‚
 β”‚                                                      β”‚
 β”‚   app.use(express.json())                            β”‚
 β”‚   β†’ reads raw body stream                           β”‚
 β”‚   β†’ parses JSON automatically                       β”‚
 β”‚   β†’ attaches to req.body                            β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                 EXPRESS ROUTER                       β”‚
 β”‚                                                      β”‚
 β”‚   app.post('/users')   βœ“  MATCHED!                   β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                 ROUTE HANDLER                        β”‚
 β”‚                                                      β”‚
 β”‚   (req, res) => {                                    β”‚
 β”‚     const { name, email } = req.body;                β”‚
 β”‚     res.status(201).json({                           β”‚
 β”‚       message: 'User created!',                      β”‚
 β”‚       user: { name, email }                          β”‚
 β”‚     });                          ← executes          β”‚
 β”‚   }                                                  β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β”‚  HTTP Response
                        β”‚  Status: 201 Created
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   CLIENT receives:                                   β”‚
 β”‚   { message: "User created!", user: { name, email }} β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Express Routing + Response Structure Visualization

EXPRESS APP
β”‚
β”œβ”€β”€ Middleware
β”‚   └── app.use(express.json())   ← parses req.body
β”‚
β”œβ”€β”€ POST  /users                  β†’ Create new user
β”‚   β”œβ”€β”€ INPUT:  req.body          ← { name, email }
β”‚   └── OUTPUT: res.status(201).json({ message, user })
β”‚
β”œβ”€β”€ POST  /login                  β†’ Authenticate user
β”‚   β”œβ”€β”€ INPUT:  req.body          ← { email, password }
β”‚   └── OUTPUT: res.status(200).json({ token })
β”‚            or res.status(401).json({ error: "Invalid" })
β”‚
β”œβ”€β”€ POST  /products               β†’ Add new product
β”‚   β”œβ”€β”€ INPUT:  req.body          ← { title, price }
β”‚   └── OUTPUT: res.status(201).json({ id, title, price })
β”‚
└── Validation Layer
    β”œβ”€β”€ Missing fields β†’ res.status(400).json({ error })
    └── Server crash   β†’ res.status(500).json({ error })

Input Validation in POST Routes

app.post('/users', (req, res) => {
  const { name, email } = req.body;

  // ─── Validate Input ──────────────────────────────
  if (!name || !email) {
    return res.status(400).json({
      error: 'Name and email are required'  // ← 400 Bad Request
    });
  }

  // ─── Process & Respond ───────────────────────────
  res.status(201).json({
    message: 'User created successfully!',
    user: { id: Date.now(), name, email }   // ← 201 Created
  });
});

4. Example β€” Complete POST + Response Handler

// index.js β€” POST Requests + Response Handling

const express = require('express');
const app = express();

app.use(express.json()); // βœ… Enable body parsing

// ─── Fake Database ────────────────────────────────
const users = [
  { id: 1, name: 'Rahul',  email: 'rahul@gmail.com'  },
  { id: 2, name: 'Priya',  email: 'priya@gmail.com'  },
];

// ─── GET all users ────────────────────────────────
app.get('/users', (req, res) => {
  res.status(200).json(users);
});

// ─── POST create user ─────────────────────────────
app.post('/users', (req, res) => {
  const { name, email } = req.body;

  // Validate
  if (!name || !email) {
    return res.status(400).json({
      error: '❌ Name and email are required'
    });
  }

  // Create
  const newUser = { id: Date.now(), name, email };
  users.push(newUser);

  // Respond
  res.status(201).json({
    message: 'βœ… User created successfully!',
    user: newUser
  });
});

// ─── POST login ───────────────────────────────────
app.post('/login', (req, res) => {
  const { email, password } = req.body;

  if (email === 'rahul@gmail.com' && password === '1234') {
    return res.status(200).json({
      message: 'βœ… Login successful!',
      token: 'abc123xyz'
    });
  }

  res.status(401).json({ error: '❌ Invalid credentials' });
});

// ─── START SERVER ─────────────────────────────────
app.listen(3000, () => {
  console.log('βœ… Server running on http://localhost:3000');
});

Test Results:

POST /users
Body: { "name": "Anjali", "email": "anjali@gmail.com" }
β†’ 201: { message: "βœ… User created!", user: { id, name, email } }

POST /users
Body: { "name": "Anjali" }         ← missing email
β†’ 400: { error: "❌ Name and email are required" }

POST /login
Body: { "email": "rahul@gmail.com", "password": "1234" }
β†’ 200: { message: "βœ… Login successful!", token: "abc123xyz" }

POST /login
Body: { "email": "wrong@gmail.com", "password": "wrong" }
β†’ 401: { error: "❌ Invalid credentials" }

5. Conclusion

POST requests and responses are the two sides of every server interaction β€” one brings data in, the other sends meaning out. Express makes POST handling effortless with express.json() middleware that auto-parses request bodies, and req.body that delivers clean, ready-to-use data instantly. Sending responses becomes equally powerful with res.json(), res.status(), and res.send() β€” giving you full control over what the client receives and why. Together, they form the foundation of every real-world feature: user registration, login, form submission, and data creation. Master these two concepts, and you can build any backend feature imaginable.


6. Sending responses

1. Introduction

A server that receives requests but cannot reply properly is useless. The response is the server's voice β€” it tells the client what happened, what was found, what went wrong, or what was created. Every interaction on the web ends with a response. But responses are more than just data β€” they carry a status code that signals success or failure, headers that describe the content, and a body that delivers the actual payload. Raw Node.js forces you to manually construct every piece of this. Express.js gives you clean, chainable, readable methods that make sending the perfect response effortless every single time.

2. Definition

Sending a response in Express means using the res object inside a route handler to communicate back to the client β€” with a status code, optional headers, and a body containing text, JSON, HTML, or nothing at all.

The Three Parts of Every Response:

HTTP Response
β”‚
β”œβ”€β”€ 1. Status Code  β†’  Tells client WHAT happened
β”‚       200 OK / 201 Created / 404 Not Found / 500 Error
β”‚
β”œβ”€β”€ 2. Headers      β†’  Tells client HOW to read the body
β”‚       Content-Type: application/json
β”‚       Content-Type: text/html
β”‚
└── 3. Body         β†’  The actual DATA sent back
        "Hello World" / { name: "Rahul" } / <h1>Page</h1>

Express Response Methods β€” Quick Map:

res.send()         β†’  Send text, HTML, or buffer
res.json()         β†’  Send JSON (sets header automatically)
res.status()       β†’  Set HTTP status code (chainable)
res.sendStatus()   β†’  Set status + send default message
res.set()          β†’  Set custom headers
res.redirect()     β†’  Redirect to another URL
res.end()          β†’  End response with no body

3. Explanation

Sending Responses in Raw Node.js (The Hard Way)

const http = require('http');

http.createServer((req, res) => {

  if (req.url === '/user' && req.method === 'GET') {

    // ❌ Step 1: Manually set status + headers
    res.writeHead(200, {
      'Content-Type': 'application/json'
    });

    // ❌ Step 2: Manually stringify JSON
    res.end(JSON.stringify({ id: 1, name: 'Rahul' }));

  } else if (req.url === '/error') {

    // ❌ Repeat boilerplate for every route
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');

  } else if (req.url === '/redirect') {

    // ❌ Manually set Location header
    res.writeHead(302, { Location: '/user' });
    res.end();
  }

}).listen(3000);

Sending Responses in Express (The Clean Way)

const express = require('express');
const app = express();

// βœ… Clean JSON response β€” headers set automatically
app.get('/user', (req, res) => {
  res.json({ id: 1, name: 'Rahul' });
});

// βœ… Clean 404 response
app.get('/error', (req, res) => {
  res.status(404).json({ error: 'Not Found' });
});

// βœ… Clean redirect
app.get('/redirect', (req, res) => {
  res.redirect('/user');
});

app.listen(3000);

Method 1 β€” res.send()

// Sends plain text
app.get('/text', (req, res) => {
  res.send('Hello World!');
});

// Sends HTML
app.get('/html', (req, res) => {
  res.send('<h1>Welcome to Express!</h1>');
});
GET /text  β†’  "Hello World!"           Content-Type: text/html
GET /html  β†’  <h1>Welcome!</h1>        Content-Type: text/html

Method 2 β€” res.json()

// Sends JSON object
app.get('/user', (req, res) => {
  res.json({ id: 1, name: 'Rahul', role: 'Developer' });
});

// Sends JSON array
app.get('/users', (req, res) => {
  res.json([
    { id: 1, name: 'Rahul'  },
    { id: 2, name: 'Priya'  },
    { id: 3, name: 'Anjali' }
  ]);
});
GET /user   β†’  { id:1, name:"Rahul", role:"Developer" }
GET /users  β†’  [ {...}, {...}, {...} ]
              Content-Type: application/json  ← auto set

Always use res.json() for APIs β€” it auto-sets Content-Type: application/json

Method 3 β€” res.status()

// βœ… Chain status with json or send
app.get('/created', (req, res) => {
  res.status(201).json({ message: 'βœ… Resource created!' });
});

app.get('/not-found', (req, res) => {
  res.status(404).json({ error: '❌ Resource not found' });
});

app.get('/server-error', (req, res) => {
  res.status(500).json({ error: 'πŸ’₯ Internal server error' });
});
GET /created      β†’  201  { message: "βœ… Resource created!" }
GET /not-found    β†’  404  { error: "❌ Resource not found" }
GET /server-error β†’  500  { error: "πŸ’₯ Internal server error" }

Method 4 β€” res.sendStatus()

// Sends ONLY the status code + its default message
app.delete('/user/:id', (req, res) => {
  res.sendStatus(204); // "No Content"
});

app.get('/forbidden', (req, res) => {
  res.sendStatus(403); // "Forbidden"
});
DELETE /user/1   β†’  204 No Content   (empty body)
GET /forbidden   β†’  403 Forbidden    (text message only)

Use sendStatus() when body content is not needed β€” like DELETE operations.

Method 5 β€” res.redirect()

// Redirect to another route
app.get('/home', (req, res) => {
  res.redirect('/');                    // 302 by default
});

// Redirect with custom status
app.get('/old-page', (req, res) => {
  res.redirect(301, '/new-page');       // 301 Permanent
});

// Redirect to external URL
app.get('/docs', (req, res) => {
  res.redirect('https://expressjs.com');
});

Method 6 β€” res.set() Custom Headers

// Set custom response headers
app.get('/custom', (req, res) => {
  res.set('X-Powered-By', 'My Express App');
  res.set('Cache-Control', 'no-cache');
  res.json({ message: 'Custom headers set!' });
});
Response Headers:
  Content-Type:  application/json
  X-Powered-By:  My Express App
  Cache-Control: no-cache

Method 7 β€” res.end()

// End response with no body β€” useful in middleware
app.get('/ping', (req, res) => {
  res.status(200).end(); // ← no body, just signal
});

Request β†’ Route Handler β†’ Response Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                    CLIENT                           β”‚
 β”‚         GET /user/5                                 β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β”‚  HTTP Request
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚               EXPRESS SERVER                        β”‚
 β”‚                                                     β”‚
 β”‚   app.get('/user/:id')  βœ“  MATCHED                  β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚               ROUTE HANDLER                         β”‚
 β”‚                                                     β”‚
 β”‚   (req, res) => {                                   β”‚
 β”‚     const user = findUser(req.params.id);           β”‚
 β”‚                                                     β”‚
 β”‚     if (!user)                                      β”‚
 β”‚       return res.status(404)                        β”‚
 β”‚                .json({ error: 'Not found' });       β”‚
 β”‚                                                     β”‚
 β”‚     res.status(200).json(user);  ← executes         β”‚
 β”‚   }                                                 β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚               HTTP RESPONSE BUILT                   β”‚
 β”‚                                                     β”‚
 β”‚   Status  β†’  200 OK                                 β”‚
 β”‚   Header  β†’  Content-Type: application/json         β”‚
 β”‚   Body    β†’  { id: "5", name: "Rahul" }             β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   CLIENT receives  β†’  { id: "5", name: "Rahul" }    β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Express Response Structure Visualization

EXPRESS ROUTE HANDLER
β”‚
β”œβ”€β”€ res.send()
β”‚   β”œβ”€β”€ string    β†’  text/html
β”‚   └── buffer    β†’  application/octet-stream
β”‚
β”œβ”€β”€ res.json()
β”‚   └── object/array  β†’  application/json  ← auto header
β”‚
β”œβ”€β”€ res.status(code)
β”‚   β”œβ”€β”€ .json({...})  β†’  status + JSON body
β”‚   β”œβ”€β”€ .send('...')  β†’  status + text body
β”‚   └── .end()        β†’  status + no body
β”‚
β”œβ”€β”€ res.sendStatus(code)
β”‚   └── code + default text  β†’  204 "No Content"
β”‚
β”œβ”€β”€ res.redirect(url)
β”‚   └── 302 (default) or custom  β†’  Location header
β”‚
└── res.set(header, value)
    └── Custom headers before sending body

Status Code β€” Full Decision Guide

Was the request successful?
β”‚
β”œβ”€β”€ YES
β”‚   β”œβ”€β”€ Did you CREATE something?   β†’  201 Created
β”‚   β”œβ”€β”€ Nothing to return?          β†’  204 No Content
β”‚   └── Normal success?             β†’  200 OK
β”‚
└── NO
    β”œβ”€β”€ CLIENT mistake?
    β”‚   β”œβ”€β”€ Bad data / missing fields?  β†’  400 Bad Request
    β”‚   β”œβ”€β”€ Not logged in?              β†’  401 Unauthorized
    β”‚   β”œβ”€β”€ Logged in, no permission?   β†’  403 Forbidden
    β”‚   └── Resource doesn't exist?     β†’  404 Not Found
    β”‚
    └── SERVER mistake?
        └── Unexpected crash/bug?       β†’  500 Internal Server Error

4. Example β€” Complete Response Handling

// index.js β€” All Response Methods in Action

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

// ─── Fake Database ─────────────────────────────────
const users = [
  { id: 1, name: 'Rahul', email: 'rahul@gmail.com' },
  { id: 2, name: 'Priya', email: 'priya@gmail.com' },
];

// ─── 200 OK β€” fetch all ────────────────────────────
app.get('/users', (req, res) => {
  res.status(200).json(users);
});

// ─── 200 / 404 β€” fetch one ─────────────────────────
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));

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

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

// ─── 201 Created β€” create user ─────────────────────
app.post('/users', (req, res) => {
  const { name, email } = req.body;

  if (!name || !email) {
    return res.status(400).json({ error: '❌ Name and email required' });
  }

  const newUser = { id: Date.now(), name, email };
  users.push(newUser);

  res.status(201).json({
    message: 'βœ… User created!',
    user: newUser
  });
});

// ─── 204 No Content β€” delete user ──────────────────
app.delete('/users/:id', (req, res) => {
  res.sendStatus(204);
});

// ─── 301 Redirect ──────────────────────────────────
app.get('/all-users', (req, res) => {
  res.redirect(301, '/users');
});

// ─── 500 Server Error ──────────────────────────────
app.get('/crash', (req, res) => {
  res.status(500).json({ error: 'πŸ’₯ Something went wrong!' });
});

// ─── START SERVER ──────────────────────────────────
app.listen(3000, () => {
  console.log('βœ… Server running on http://localhost:3000');
});

Test Results:

GET    /users          β†’  200  [ { id:1, name:"Rahul" }, ... ]
GET    /users/1        β†’  200  { id:1, name:"Rahul", email:"..." }
GET    /users/99       β†’  404  { error: "❌ User not found" }

POST   /users
Body:  { name:"Anjali", email:"anjali@gmail.com" }
                       β†’  201  { message:"βœ… User created!", user:{...} }

POST   /users
Body:  { name:"Anjali" }
                       β†’  400  { error: "❌ Name and email required" }

DELETE /users/1        β†’  204  (empty body)
GET    /all-users      β†’  301  β†’ redirects to /users
GET    /crash          β†’  500  { error: "πŸ’₯ Something went wrong!" }

5. Conclusion

Sending the right response is not just about returning data β€” it is about communicating clearly with the client. Every response should tell a complete story: the status code says what happened, the headers say how to read it, and the body delivers the payload. Express gives you a clean, chainable toolkit β€” res.json(), res.status(), res.send(), res.redirect() β€” that makes building clear, consistent API responses effortless. A well-designed response layer is the difference between an API that is frustrating to work with and one that is a pleasure to consume. Always send the right code. Always send the right data. Always let the response tell the full story.