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.paramsholds 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 viareq.body.
Sending a response means the server communicates back to the client using methods like
res.send(),res.json(), orres.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
resobject 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.





