Express.js Route Parameters and Query Strings
Web applications need to work with dynamic data in URLs. A product page needs to know which product to display. A search page needs to know what the user typed. Express handles this through two mechanisms: route parameters and query strings. Both let clients send information to the server through the URL, but they serve different purposes and follow different formats.
The Library Catalog Analogy
Route parameters are like a specific shelf number at a library — you go directly to shelf 47B to find one exact book. Query strings are like search filters at the reference desk — you ask for books by subject, year, and language to narrow down a list. Both get you information, but one retrieves a single known item and the other filters a collection.
Route Parameter (one specific resource): GET /books/978-0-06-112008-4 ← ISBN is the parameter Query String (filter a collection): GET /books?genre=fiction&year=2023&language=english
Route Parameters
Route parameters are named placeholders in the URL path. Define them with a colon prefix in the route path:
app.get('/users/:userId', (req, res) => {
const id = req.params.userId;
res.send(`Fetching user with ID: ${id}`);
});
When someone visits /users/42, Express extracts 42 and puts it in req.params.userId.
Multiple Parameters in One Route
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({
user: userId,
post: postId
});
});
Visiting /users/5/posts/99 gives you req.params = { userId: '5', postId: '99' }.
Parameter Types: Always Strings
Route parameters always arrive as strings, even if the URL contains numbers. Convert them when needed:
app.get('/products/:id', (req, res) => {
const id = parseInt(req.params.id); // Convert string to number
if (isNaN(id)) {
return res.status(400).json({ error: 'ID must be a number' });
}
res.json({ productId: id });
});
Optional Route Parameters
Add a question mark after the parameter name to make it optional:
app.get('/blog/:year/:month?', (req, res) => {
const { year, month } = req.params;
if (month) {
res.send(`Posts from ${month}/${year}`);
} else {
res.send(`Posts from the year ${year}`);
}
});
This route matches both /blog/2024 and /blog/2024/march.
How Route Parameters Appear in URLs
Route Definition URL Example req.params
──────────────────────────────────────────────────────────────
/users/:id /users/42 { id: '42' }
/orders/:orderId /orders/ORDER-999 { orderId: 'ORDER-999' }
/files/:name.:ext /files/photo.jpg { name: 'photo', ext: 'jpg' }
/shop/:cat/:item /shop/shoes/sneakers { cat: 'shoes', item: 'sneakers' }
Query Strings
Query strings appear at the end of a URL after a ?. They carry key-value pairs separated by &. Use them for optional filters, search terms, sorting, and pagination rather than for identifying a specific resource.
URL: /products?category=shoes&color=red&sort=price&page=2
app.get('/products', (req, res) => {
console.log(req.query);
// {
// category: 'shoes',
// color: 'red',
// sort: 'price',
// page: '2'
// }
});
Query String Anatomy
/search?q=express+routing&limit=10&offset=20
│ │ │
│ │ └── offset = '20'
│ └── limit = '10'
└── q = 'express routing'
(+ decodes to a space)
Using Query Parameters for Filtering
app.get('/products', (req, res) => {
const { category, minPrice, maxPrice, sort, page = 1 } = req.query;
// Build a filter message (in real app, you'd query a database)
const filter = {
category: category || 'all',
priceRange: `${minPrice || 0} - ${maxPrice || 'any'}`,
sortBy: sort || 'default',
currentPage: parseInt(page)
};
res.json({ filters: filter, results: [] });
});
The = 1 default value ensures the page number always has a fallback when not provided.
Array Values in Query Strings
Send multiple values for the same key by repeating the key:
URL: /products?color=red&color=blue&color=green
app.get('/products', (req, res) => {
console.log(req.query.color);
// ['red', 'blue', 'green']
});
Route Parameters vs Query Strings: When to Use Each
┌──────────────────────────────────────────────────────────────┐ │ Route Parameters vs Query Strings │ ├──────────────────────┬───────────────────────────────────────┤ │ Route Parameters │ Query Strings │ ├──────────────────────┼───────────────────────────────────────┤ │ Required to identify │ Optional extras that modify behavior │ │ a specific resource │ │ │ │ │ │ Part of the path │ After the ? symbol │ │ │ │ │ /users/42 │ /users?role=admin&page=2 │ │ │ │ │ Always required │ Can have default values │ │ │ │ │ Use for: IDs, │ Use for: search, filter, sort, │ │ slugs, categories │ pagination, optional fields │ └──────────────────────┴───────────────────────────────────────┘
Combining Both in One Route
A route can use both parameters and query strings at the same time:
// GET /users/42/orders?status=shipped&page=3
app.get('/users/:userId/orders', (req, res) => {
const { userId } = req.params;
const { status = 'all', page = 1 } = req.query;
res.json({
user: userId,
orderStatus: status,
page: parseInt(page)
});
});
The user ID identifies whose orders to fetch. The query string filters and paginates those orders.
Validating Parameters and Query Values
Never trust URL input directly. Always validate before using:
app.get('/posts/:id', (req, res) => {
const id = req.params.id;
// Validate: must be a positive integer
if (!/^\d+$/.test(id)) {
return res.status(400).json({ error: 'Invalid post ID' });
}
const page = parseInt(req.query.page) || 1;
// Validate: page must be between 1 and 100
if (page < 1 || page > 100) {
return res.status(400).json({ error: 'Page must be between 1 and 100' });
}
res.json({ postId: parseInt(id), page });
});
URL Encoding in Query Strings
Special characters in query string values get encoded automatically by browsers. Express decodes them for you:
URL in browser: /search?q=express%20js%20tutorial req.query.q → 'express js tutorial' (decoded automatically) URL in browser: /filter?name=O%27Brien req.query.name → "O'Brien" (apostrophe decoded)
Summary
Route parameters identify a specific resource and appear as named segments in the URL path with a colon prefix (:id). Access them through req.params. Query strings carry optional key-value pairs after the ? symbol and work best for filters, sorting, and pagination. Access them through req.query. Use route parameters for required identifiers and query strings for optional modifiers. Always validate and convert values from both sources before using them in your logic or database queries, since all incoming URL values arrive as strings.
