Static routes like /about only get you so far. Real applications need URLs that carry data: /users/42, /products/laptops/8, or /search?q=express&page=2. Express gives you two tools for this: route parameters for identifying resources and query strings for optional modifiers. This lesson covers both, plus validation basics.
A route parameter is a named URL segment that starts with a colon. Whatever the client puts in that position is captured into req.params:
app.get('/users/:id', (req, res) => {
res.send('User ID is ' + req.params.id);
});Requesting /users/42 responds with 'User ID is 42', and /users/alice responds with 'User ID is alice'. The parameter matches a single segment, so /users/42/posts does not match this route.
You can capture several segments at once. Each name becomes a property on req.params:
app.get('/products/:category/:productId', (req, res) => {
const { category, productId } = req.params;
res.json({ category, productId });
});A request to /products/laptops/8 yields { category: 'laptops', productId: '8' }. Note that captured values are always strings, even when they look like numbers. Convert with Number() before doing math or database lookups by numeric ID.
Here is a pattern you will use constantly: find an item by ID and return 404 when it does not exist.
const users = [
{ id: 1, name: 'Aisha' },
{ id: 2, name: 'Marco' },
];
app.get('/api/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);
});The early return is important. Without it, execution would continue and try to send a second response.
Everything after the ? in a URL is the query string: /search?q=express&page=2. Express parses it into req.query automatically:
app.get('/search', (req, res) => {
const q = req.query.q || '';
const page = Number(req.query.page) || 1;
res.json({ query: q, page });
});Query values are also strings and, crucially, always optional. Your handler must supply sensible defaults, as the example does with an empty search and page 1.
Use a route parameter when the value identifies the resource itself: /users/42 means user number 42; without the ID the URL is meaningless. Use the query string for optional instructions about how to return data: sorting, filtering, pagination, or search terms. A good mental test: if removing the value should produce a 404, it belongs in the path; if removing it should just change the output, it belongs in the query.
Never trust URL input. A quick guard prevents confusing errors deeper in your code:
app.get('/api/orders/:id', (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id) || id <= 0) {
return res.status(400).json({ error: 'id must be a positive integer' });
}
res.json({ orderId: id });
});Later in the series we will move validation into reusable middleware.
Next lesson: Middleware Explained, the concept that makes Express truly powerful.