Difficulty: Beginner
Routing is the mechanism by which an application responds to client requests at specific URLs (endpoints) using specific HTTP methods. In Express, you define routes using methods on the app object that correspond to HTTP methods: app.get() for GET requests, app.post() for POST, app.put() for PUT, app.delete() for DELETE, and so on. Each route takes a path pattern and one or more handler functions.
HTTP methods have semantic meaning in RESTful APIs. GET retrieves data and should never modify server state. POST creates new resources. PUT replaces an existing resource entirely. PATCH partially updates a resource. DELETE removes a resource. Following these conventions makes your API predictable and intuitive to consume. Express also provides app.all() which matches any HTTP method, useful for middleware that should run on all requests to a particular path.
Route parameters are named URL segments prefixed with a colon. For example, the route '/users/:id' matches '/users/1', '/users/42', and '/users/abc'. The captured value is available on req.params - in this case, req.params.id. You can have multiple parameters in a single route: '/users/:userId/posts/:postId' captures both values. Route parameters always produce string values, so you must parse them to numbers with parseInt() when needed.
Query strings are appended to the URL after a '?' character and are used for optional parameters like filtering, sorting, and pagination. In Express, query string parameters are automatically parsed and available on req.query as an object. For the URL '/users?role=admin&page=2', req.query would be { role: 'admin', page: '2' }. Note that query values are always strings.
Express matches routes in the order they are defined. The first matching route handles the request, and subsequent matching routes are ignored (unless the handler calls next()). This means more specific routes should be defined before more general ones. For example, '/users/new' should be defined before '/users/:id', otherwise a request to '/users/new' would be captured by the parameter route with id='new'.
const express = require('express');
const app = express();
app.use(express.json());
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
let nextId = 3;
// GET all users
app.get('/api/users', (req, res) => {
res.json(users);
});
// GET single user by ID
app.get('/api/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);
});
// POST create user
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) return res.status(400).json({ error: 'Name and email required' });
const user = { id: nextId++, name, email };
users.push(user);
res.status(201).json(user);
});
// PUT update user (full replacement)
app.put('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'User not found' });
users[index] = { id: parseInt(req.params.id), ...req.body };
res.json(users[index]);
});
// DELETE user
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'User not found' });
users.splice(index, 1);
res.json({ message: 'User deleted' });
});
app.listen(3000, () => console.log('API on port 3000'));
This is a complete RESTful CRUD API. Each HTTP method maps to a CRUD operation: GET=Read, POST=Create, PUT=Update, DELETE=Delete. Route parameters (:id) capture dynamic URL segments. Status codes communicate the result: 201 for created, 404 for not found, 400 for bad input.
const express = require('express');
const app = express();
const products = [
{ id: 1, name: 'Laptop', category: 'electronics', price: 999 },
{ id: 2, name: 'Shirt', category: 'clothing', price: 29 },
{ id: 3, name: 'Phone', category: 'electronics', price: 699 },
{ id: 4, name: 'Pants', category: 'clothing', price: 49 },
{ id: 5, name: 'Tablet', category: 'electronics', price: 499 },
{ id: 6, name: 'Jacket', category: 'clothing', price: 89 }
];
// GET /api/products?category=electronics&minPrice=500&page=1&limit=2
app.get('/api/products', (req, res) => {
let result = [...products];
// Filter by category
if (req.query.category) {
result = result.filter(p => p.category === req.query.category);
}
// Filter by minimum price
if (req.query.minPrice) {
result = result.filter(p => p.price >= parseInt(req.query.minPrice));
}
// Pagination
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const startIndex = (page - 1) * limit;
const paginatedResult = result.slice(startIndex, startIndex + limit);
res.json({
data: paginatedResult,
total: result.length,
page,
totalPages: Math.ceil(result.length / limit)
});
});
app.listen(3000, () => console.log('Product API on port 3000'));
Query strings handle optional parameters like filtering, sorting, and pagination. req.query automatically parses the URL query string into an object. All query values are strings, so use parseInt() for numeric comparisons.
const express = require('express');
const app = express();
// app.all() matches ANY HTTP method
app.all('/api/*', (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // Pass to the next matching route
});
// Specific route MUST come before parameter route
app.get('/api/users/me', (req, res) => {
res.json({ id: 1, name: 'Current User' });
});
// This would catch '/users/me' if defined first!
app.get('/api/users/:id', (req, res) => {
res.json({ id: req.params.id, name: `User ${req.params.id}` });
});
// Route chaining with app.route()
app.route('/api/posts')
.get((req, res) => {
res.json([{ id: 1, title: 'First Post' }]);
})
.post((req, res) => {
res.status(201).json({ id: 2, title: 'New Post' });
});
app.listen(3000, () => console.log('Server on port 3000'));
Route order matters: '/users/me' must be defined before '/users/:id' or the parameter route will capture 'me' as an id. app.all() is useful for logging or auth checks on all methods. app.route() chains multiple method handlers for the same path.
GET, POST, PUT, DELETE, Route Parameters, Query Strings, Route Methods