Hello readers 👋, welcome to the 11th blog in our Node.js series!
We've come a long way. We started by setting up Node.js, understood its event loop, explored blocking vs non‑blocking code, handled async operations with promises, secured routes with JWT, designed a REST API, and even dove into middleware. But to build those APIs and middleware, we used Express.js. Now it's time to shine a focused light on exactly how Express helps us create routes and handle requests so effortlessly.
In this post, we'll take a step back and talk about what Express.js is, why it makes Node.js development so much smoother, and how to create clean, readable routes for GET and POST requests. By the end, you'll see why Express has become the go‑to framework for building web servers with Node.js.
Let's jump in.
What is Express.js?
Express.js is a minimal and flexible web application framework for Node.js. It provides a thin layer of fundamental web application features, without obscuring Node.js features that you know and love. In simpler words, it's a set of tools that makes it ridiculously easy to handle HTTP requests, define routes, work with middleware, and send responses.
At its core, Express is just a function that returns an application object. You attach route handlers to it, and it efficiently maps incoming requests to the right handlers.
Why Express simplifies Node.js development
To appreciate Express, let's quickly compare it with building an HTTP server using Node.js's built‑in http module.
Raw Node.js HTTP server example:
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'GET' && req.url === '/users') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([{ id: 1, name: 'Satya' }]));
} else if (req.method === 'GET' && req.url.startsWith('/users/')) {
const id = req.url.split('/')[2];
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ id, name: 'Satya' }));
} else if (req.method === 'POST' && req.url === '/users') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const user = JSON.parse(body);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(user));
});
} else {
res.writeHead(404);
res.end('Not found');
}
});
server.listen(3000);
That works, but as your application grows, the code becomes a mess of if‑else statements, manual body parsing, and repetitive header writing.
Now look at the same logic with Express:
const express = require('express');
const app = express();
app.use(express.json());
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'Satya' }]);
});
app.get('/users/:id', (req, res) => {
const id = req.params.id;
res.json({ id, name: 'Satya' });
});
app.post('/users', (req, res) => {
const user = req.body;
res.status(201).json(user);
});
app.listen(3000);
Here's what Express gives you:
-
Clean routing: You directly map HTTP methods and URL patterns to handler functions. No need to manually parse
req.urlor checkreq.method. -
Automatic body parsing: With
express.json()middleware, the body is parsed and available atreq.body. -
Easy response helpers:
res.json()automatically sets the content type and stringifies the object. -
Route parameters: Express extracts
:idfrom the URL and places it inreq.params. - Middleware support: You can chain functions (like we explored in the previous blog) to handle logging, auth, validation.
Express takes away the boilerplate so you can focus on your application logic.
Creating your first Express server
Let's start from zero. First, initialize a project and install Express:
npm init -y
npm install express
Then create a file, say server.js:
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('Hello from Express!');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Run it with node server.js, and open http://localhost:3000. You'll see the greeting. This is the absolute minimal Express server.
Handling GET requests
GET requests are used to fetch data. Express makes it very intuitive.
1. Simple GET route
app.get('/hello', (req, res) => {
res.send('Hello World');
});
2. Using route parameters
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`User ID: ${userId}`);
});
A request to /users/42 returns User ID: 42. Express automatically parses the :id segment.
3. Query string parameters
app.get('/search', (req, res) => {
const query = req.query.q;
const page = req.query.page || 1;
res.json({ query, page });
});
A request to /search?q=express&page=2 gives { "query": "express", "page": "2" }.
4. Returning JSON data
app.get('/api/users', (req, res) => {
const users = [
{ id: 1, name: 'Satya' },
{ id: 2, name: 'Priya' }
];
res.json(users); // sets Content-Type to application/json automatically
});
res.json() is the go‑to for API responses.
Handling POST requests
POST requests typically create new resources. To process them, you need to read the request body. Express's built-in middleware express.json() does exactly that.
app.use(express.json()); // parse JSON bodies
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
// In a real app, you'd save to a database
const newUser = { id: Date.now(), name, email };
res.status(201).json(newUser);
});
Test it with a client that sends JSON:
{
"name": "Rahul",
"email": "rahul@example.com"
}
The response will be 201 Created with the new user object.
You can also handle form submissions using express.urlencoded({ extended: true }) if you're dealing with traditional HTML forms.
Sending responses
Express provides multiple methods to send responses, each suitable for different scenarios:
-
res.send(string)– sends a plain text or HTML string; setsContent-Typeaccordingly. -
res.json(object)– sends a JSON object with proper header. -
res.status(code).json(object)– sets the status code and sends JSON. -
res.sendFile(path)– streams a file from disk. -
res.redirect(url)– redirects to another URL. -
res.download(path)– prompts the browser to download a file.
You can also chain status codes: res.status(404).send('Not Found').
Example:
app.get('/file', (req, res) => {
res.sendFile(__dirname + '/sample.pdf');
});
Express is flexible and doesn't force one way of responding.
Conclusion
Express.js strips away the repetitive plumbing of raw Node.js HTTP servers and lets you define routes declaratively, making your code cleaner and faster to write. With just a few lines, you can set up a server, handle GET and POST requests, parse inputs, and send proper responses. This is the foundation of every Node.js backend I've built.
Let's recap:
- Express is a minimal framework that simplifies creating HTTP servers in Node.js.
- It offers clean routing with methods like
app.get,app.post,app.put, etc. - Route parameters (
req.params), query strings (req.query), and body parsing (req.body) are built in. - Response helpers (
res.json,res.send,res.status) make sending data back straightforward. - Compared to raw Node, Express eliminates boilerplate and reduces developer friction.
Now that you're comfortable with routes and request handling, the next step is to build more complex applications, connect to databases, and structure larger codebases. We'll continue that journey in the next post.
Hope you found this helpful! If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X, where I post more about web development.
Top comments (0)