Skip to content

ExpressJS - URL Building

While static routes like /about or /contact are useful, most web applications require dynamic routes to handle variable data. For example, a route to display a user’s profile needs to accept a user ID. Express makes it easy to build dynamic URLs by defining route parameters.

Route parameters are named segments of the URL, prefixed with a colon (:). These segments capture the values specified at their position in the URL. The captured values are populated in the req.params object.

Here is a modern example of a dynamic route using ES6 syntax:

import express from 'express';
const app = express();
const PORT = 3000;
// Defines a route with a dynamic 'id' parameter
app.get('/users/:id', (req, res) => {
// Destructuring is a clean way to access parameters
const { id } = req.params;
res.send(`You requested the profile for User ID: ${id}`);
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

To test this, run the server and navigate to http://localhost:3000/users/123. The browser will display:

You requested the profile for User ID: 123

You can replace 123 with any other value, and the response will update dynamically.

You can define multiple parameters in a single route. They will all be available on the req.params object.

import express from 'express';
const app = express();
const PORT = 3000;
// Route with multiple parameters for a blog post
app.get('/posts/:category/:slug', (req, res) => {
const { category, slug } = req.params;
res.send(`Fetching post with slug "${slug}" in the "${category}" category.`);
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

To test this, navigate to http://localhost:3000/posts/tech/modern-javascript-features. The server will respond with:

Fetching post with slug "modern-javascript-features" in the "tech" category.

Sometimes you need to enforce a specific format for a route parameter. You can add a regular expression in parentheses () after the parameter name to validate its format. If the regex doesn’t match, the route will not be triggered.

Let’s say a user ID must be a sequence of digits. You can enforce this with the regex ([0-9]+).

import express from 'express';
const app = express();
const PORT = 3000;
// This route will only match if 'id' is a number.
app.get('/items/:id([0-9]+)', (req, res) => {
const { id } = req.params;
res.send(`Item ID: ${id}`);
});
// A fallback for any other requests to /items/*
app.get('/items/*', (req, res) => {
res.status(400).send('Invalid Item ID format. ID must be numeric.');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

In this example:

  • A request to /items/456 will be handled by the first route.
  • A request to /items/abc will fail the regex match and be handled by the second, more generic route, returning a 400 Bad Request error.

If a request does not match any of your defined routes, Express will automatically respond with a 404 Not Found error. It’s a best practice to define a custom 404 handler to provide a user-friendly response. This is done using a catch-all middleware placed at the very end of your route definitions.

import express from 'express';
const app = express();
const PORT = 3000;
// ... all your other routes go here ...
app.get('/', (req, res) => res.send('Welcome!'));
// Catch-all 404 handler
app.use((req, res) => {
res.status(404).send('Sorry, the page you are looking for does not exist.');
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});

Important: Express processes routes and middleware in the order they are defined. The 404 handler must be the last one added to the app stack to ensure it only catches requests that haven’t matched any other route.