Creating Routes & Handling Requests with Express.js
If you’ve ever tried building a web server using only built-in Node.js modules, you know it can get complicated quickly. Enter Express.js, the most popular web framework for Node.js. It’s designed to make building web applications and APIs much faster and easier.
What is Express.js?
Imagine you want to open a restaurant. You could build every piece of kitchen equipment from scratch — the oven, the refrigerator, the dishwasher. Or, you could buy reliable appliances that already exist and focus on cooking great food.
Express.js is like those appliances — for web servers. It is a framework (a toolbox of ready-made code) that sits on top of Node.js and makes it dramatically easier to handle web requests, define URL routes, and send responses back to users.
📌 Definition
Express.js is a minimal and flexible web framework for Node.js. It provides tools for creating web servers, defining URL routes, and sending responses back to clients (like browsers or mobile apps).
Express.js was created in 2010 and is still the most widely used framework for Node.js today because it’s minimal, flexible, and supported by a large community. When someone visits a website or uses an app, their browser sends an HTTP request to a server, and Express makes it easy for the server to understand that request and send back the correct response—whether it’s an HTML page, data, or a simple status message.
Why Express simplifies Node.js development
Node.js can build web servers on its own — but the code gets verbose and repetitive fast. Express wraps all that boilerplate so you write less and do more.
Let's compare the same task — creating a simple web server that says "Hello!" — using raw Node.js versus Express. This will show you exactly what Express saves you from writing.
Without Express (Raw Node)
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!');
} else {
res.writeHead(404);
res.end('Not Found');
}
}
);
server.listen(3000);
With Express
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello!');
});
app.listen(3000);
Both do the same thing. But Express is shorter, cleaner, and easier to read. And as your app grows with dozens of routes, the difference becomes even more dramatic. Express also handles 404 errors, JSON parsing, and much more — automatically.
💡 Key Benefits
Express gives you clean route definitions, built-in response helpers like
res.json(), automatic error handling, and a huge ecosystem of plugins called "middleware."
Creating your first Express server
Let's build a working Express server from scratch. You'll need Node.js installed on your computer. If you haven't already, download it from nodejs.org.
- Create a new project folder
Open your terminal and run these commands to create a project and install Express.
// Create a new folder for your project
mkdir my-express-app
cd my-express-app
// Set up the project (creates a package.json file)
npm init -y
// Install Express
npm install express
- Create your server file
Create a file calledserver.jsand add the following code.
// Step 1: Import Express
const express = require('express');
// Step 2: Create an Express "app" (your server)
const app = express();
// Step 3: Define a route (more on this soon!)
app.get('/', (req, res) => {
res.send('Hello from Express!');
});
// Step 4: Start listening on port 3000
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
- Run your server
In the terminal, runnode server.jsthen open your browser and go tohttp://localhost:3000. You should see "Hello from Express! ".
📖 Understanding the code
require('express')loads the library.express()creates your app.app.listen(3000)tells your server to watch for requests on port 3000 — like saying "stand at door number 3000 and wait for visitors."
Handling GET requests
A GET request is what happens when you type a URL into your browser. You're asking the server: "Give me this page or this data." It's the most common type of request.
In Express, you define a GET route using app.get(). It takes two things: the URL path (like /about) and a handler function that decides what to send back.
const express = require('express');
const app = express();
// Homepage — responds with a simple welcome message
app.get('/', (req, res) => {
res.send('Welcome to the homepage!');
});
// /about — responds with a different message
app.get('/about', (req, res) => {
res.send('This is the about page.');
});
// /users — responds with a list of users (as JSON)
app.get('/users', (req, res) => {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
res.json(users); // Send back JSON data
});
app.listen(3000)
Route parameters — making URLs dynamic
What if you want a route that works for any user ID — like /users/1 or /users/42? Use a colon (:) to mark a dynamic segment:
app.get('/users/:id', (req, res) => {
// req.params.id holds whatever was in the URL
const userId = req.params.id;
res.send(`You requested user with ID: ${userId}`);
});
// Visiting /users/5 → "You requested user with ID: 5"
// Visiting /users/99 → "You requested user with ID: 99"
Understanding req and res
Every route handler receives two special objects. Think of them as the two sides of a conversation:
| Object | Full name | What it contains | Example use |
|---|---|---|---|
req |
request | What the client sent to you | req.params.id, req.query.name |
res |
response | What you send back to the client | res.send(), res.json() |
Handling POST requests
A POST request is used to send data to the server — like submitting a form, creating a new account, or adding an item to a database. Unlike GET requests, POST requests carry a body of data.
Before your routes can read the body of a POST request, you need to add one line of setup — a middleware that tells Express to parse incoming JSON data automatically:
const express = require('express');
const app = express();
// ✅ This middleware lets us read JSON data sent in the request body
app.use(express.json());
// A POST route to create a new user
app.post('/users', (req, res) => {
const { name, email } = req.body; // read the data sent by the client
// In a real app you'd save to a database here
console.log(`New user: \({name}, \){email}`);
res.status(201).json({
message: 'User created!',
user: { name, email }
});
});
app.listen(3000);
📦 What is middleware?
Middleware is code that runs between receiving a request and sending a response.
express.json()is built-in middleware that reads the raw request body and automatically converts it into a JavaScript object available atreq.body.
Testing your POST route
You can't test a POST route by just typing in a browser (that only sends GET requests). Use a tool like Postman or Requestlyto send a POST request with a JSON body like this:
{
"name": "Alice",
"email": "alice@example.com"
}
// Expected response:
{
"message": "User created!",
"user": {
"name": "Alice",
"email": "alice@example.com"
}
}
Sending responses
Once Express matches a route, you need to send something back. The res object has several methods depending on what kind of response you want to send.
| Method | What it sends | Common use case |
|---|---|---|
res.send() |
Text or HTML string | Simple messages, HTML pages |
res.json() |
JSON data | APIs returning objects or arrays |
res.status() |
Sets the HTTP status code | Always chain before .send() or .json() |
res.sendFile() |
A file from the server | Serving HTML files, images, PDFs |
// 1. Send plain text
app.get('/hello', (req, res) => {
res.send('Hello, World!');
});
// 2. Send JSON data (perfect for APIs)
app.get('/api/user', (req, res) => {
res.json({
name: 'Alice',
age: 28,
role: 'developer'
});
});
// 3. Set a status code + send a message
app.get('/secret', (req, res) => {
res.status(403).json({ error: 'Access denied' });
});
// 4. Catch-all 404 handler (always put this LAST)
app.use((req, res) => {
res.status(404).send('Page not found!');
});
✅ Best practice
Always chain
res.status()before.send()or.json()when you need to set a specific status code. The default status is 200 (success), but use 201 for created resources, 400 for bad requests, 404 for not found, and 500 for server errors.
Putting it all together
const express = require('express');
const app = express();
app.use(express.json()); // enable JSON parsing
// In-memory "database" (just a JS array for this demo)
let users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
// GET all users
app.get('/users', (req, res) => {
res.json(users);
});
// GET one user by ID
app.get('/users/:id', (req, res) => {
const id = Number(req.params.id)
const user = users.find(u => u.id === id);
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
// POST create a new user
app.post('/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name
};
users.push(newUser);
res.status(201).json(newUser);
});
// 404 catch-all (must be LAST)
app.use((req, res) => {
res.status(404).send('Route not found');
});
app.listen(3000, () =>
console.log('🚀 Server running at http://localhost:3000')
);
Express Routing Structure at a Glance
Here's how a real Express app's routes might be laid out. Each URL pattern + HTTP method combination maps to exactly one handler function.
Express App — Route Map
| Method | Route | Description |
|---|---|---|
| GET | / |
Return the homepage or welcome message |
| GET | /users |
Return a list of all users |
| GET | /users/:id |
Return a single user by their ID |
| POST | /users |
Create a new user from request body |
| PUT | /users/:id |
Update an existing user by ID |
| DELETE | /users/:id |
Delete a user by ID |
Cheat sheet
Here's a summary of everything covered in this guide:
| What you want to do | The Express way |
|---|---|
| Create a server | const app = express() |
| Handle a GET request | app.get('/path', handler) |
| Handle a POST request | app.post('/path', handler) |
| Read URL parameters | req.params.id |
| Read POST body data | req.body (after app.use(express.json())) |
| Send a text response | res.send('text') |
| Send a JSON response | res.json({ key: 'value' }) |
| Set a status code | res.status(201).json(...) |
| Start the server | app.listen(3000) |
