Difficulty: Intermediate
The Express request object (req) is an enhanced version of Node.js's http.IncomingMessage. It contains all the information about the incoming HTTP request: the URL, HTTP method, headers, body, cookies, and more. Mastering the request object is essential because every route handler begins by extracting data from it.
req.params contains route parameters - named URL segments captured by patterns like ':id' or ':slug'. For the route '/users/:id/posts/:postId', a request to '/users/5/posts/42' produces req.params = { id: '5', postId: '42' }. All parameter values are strings, so parse them with parseInt() or Number() for numeric comparisons. Route parameters are used to identify specific resources in RESTful APIs.
req.query contains the parsed query string parameters. For the URL '/search?q=express&page=2&sort=date', req.query = { q: 'express', page: '2', sort: 'date' }. Like route params, all values are strings. Query strings are used for optional parameters like filtering, pagination, sorting, and search terms. Express automatically parses the query string using the 'qs' library, which supports nested objects and arrays.
req.body contains the parsed request body, but only if you have registered a body-parsing middleware like express.json() or express.urlencoded(). Without body parsing middleware, req.body is undefined. The body is used for sending data in POST, PUT, and PATCH requests - things like creating a new user, updating a profile, or submitting a form.
req.headers is an object containing all HTTP headers sent by the client, with header names lowercased. Common headers include 'content-type' (the format of the request body), 'authorization' (authentication tokens), 'user-agent' (the client software), and 'accept' (what response formats the client can handle). Express also provides convenience properties: req.hostname gives the host, req.ip gives the client IP address, req.protocol gives 'http' or 'https', and req.secure is true for HTTPS requests.
const express = require('express');
const app = express();
app.use(express.json());
// Route parameters
app.get('/api/users/:userId/posts/:postId', (req, res) => {
res.json({
params: req.params,
userId: parseInt(req.params.userId),
postId: parseInt(req.params.postId)
});
});
// GET /api/users/5/posts/42
// => { params: { userId: '5', postId: '42' }, userId: 5, postId: 42 }
// Query strings
app.get('/api/search', (req, res) => {
const {
q = '',
page = '1',
limit = '10',
sort = 'createdAt'
} = req.query;
res.json({
query: req.query,
parsed: {
searchTerm: q,
page: parseInt(page),
limit: parseInt(limit),
sort
}
});
});
// GET /api/search?q=express&page=2&sort=name
// Request body
app.post('/api/users', (req, res) => {
const { name, email, age } = req.body;
res.status(201).json({ id: 1, name, email, age });
});
app.listen(3000, () => console.log('Server on port 3000'));
Route parameters capture URL segments, query strings carry optional filters, and the body contains submitted data. Destructuring with defaults is a clean pattern for extracting and defaulting query parameters.
const express = require('express');
const app = express();
// Middleware to log all request details
app.use((req, res, next) => {
console.log('--- Request Details ---');
console.log('Method:', req.method);
console.log('Path:', req.path);
console.log('Full URL:', req.originalUrl);
console.log('Protocol:', req.protocol);
console.log('Hostname:', req.hostname);
console.log('IP:', req.ip);
console.log('Secure:', req.secure);
next();
});
app.get('/api/request-info', (req, res) => {
res.json({
method: req.method,
path: req.path,
originalUrl: req.originalUrl,
protocol: req.protocol,
hostname: req.hostname,
ip: req.ip,
headers: {
contentType: req.headers['content-type'],
authorization: req.headers['authorization'],
userAgent: req.headers['user-agent'],
accept: req.headers['accept']
},
// Express convenience methods
is_json: req.is('json'),
accepts_html: req.accepts('html'),
accepts_json: req.accepts('json')
});
});
app.listen(3000, () => console.log('Server on port 3000'));
req.path gives just the URL path without query string. req.originalUrl includes the full URL with query string. req.is() checks the Content-Type. req.accepts() performs content negotiation by checking what the client can handle.
const express = require('express');
const app = express();
app.use(express.json());
// Advanced search endpoint using all request data sources
app.get('/api/products/:category', (req, res) => {
// Route parameter for the resource category
const { category } = req.params;
// Query string for filtering and pagination
const page = Math.max(1, parseInt(req.query.page) || 1);
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 20));
const sortBy = req.query.sort || 'price';
const order = req.query.order === 'desc' ? 'desc' : 'asc';
const minPrice = parseFloat(req.query.minPrice) || 0;
const maxPrice = parseFloat(req.query.maxPrice) || Infinity;
// Headers for authentication and content negotiation
const authToken = req.headers['authorization'];
const isAuthenticated = !!authToken;
res.json({
category,
filters: { minPrice, maxPrice, sortBy, order },
pagination: { page, limit, offset: (page - 1) * limit },
authenticated: isAuthenticated,
results: [] // Would be populated from database
});
});
// GET /api/products/electronics?page=2&limit=10&sort=price&order=desc&minPrice=100
app.listen(3000, () => console.log('Server on port 3000'));
Real-world endpoints combine multiple data sources. Route params identify the resource, query strings provide optional filtering/pagination, and headers carry auth tokens. Always validate and sanitize values with defaults, min/max bounds, and type conversion.
req.params, req.query, req.body, req.headers, req.cookies, req.method, req.path