Skip to content

Express Route Parameters

Route parameters let a single route definition handle an entire family of URLs that share a shape, like /users/1, /users/2, and /users/42. This lesson covers how to define, read, and validate them.

What Are Route Parameters?

A route parameter is a named segment of a URL path, written with a leading colon, :id, :userId, that Express captures and exposes on req.params. The captured values are always strings, even if they look numeric.

Route parameters are essential for building resource-based APIs, where each individual record needs its own URL.

app.get('/users/:id', (req, res) => {
  res.json({ requestedId: req.params.id });
});

// GET /users/42 -> { "requestedId": "42" }

req.params.id is always a string, '42', not the number 42, cast it with Number() when you need arithmetic.

Route Parameter Syntax

app.get('/users/:id', handler);
app.get('/users/:userId/posts/:postId', handler);
app.get('/files/:name.:ext', handler);
app.get('/products/:id?', handler); // optional
  • Any path segment starting with : becomes a named parameter on req.params.
  • You can define multiple parameters in a single path.
  • A trailing ? on a parameter name makes that segment optional.
  • In Express 5, advanced patterns use named wildcards like :splat* instead of bare *.

Route Parameters Cheatsheet

Common parameter patterns and what ends up in req.params.

Path Pattern Example URL req.params
/users/:id /users/7 { id: '7' }
/users/:userId/orders/:orderId /users/7/orders/3 { userId: '7', orderId: '3' }
/products/:id? /products { id: undefined }
/files/:name.:ext /files/report.pdf { name: 'report', ext: 'pdf' }

Validating and Casting Parameters

Because req.params values are always strings, validate and cast them before using them in database queries or business logic, especially for numeric IDs.

app.get('/users/:id', (req, res) => {
  const id = Number(req.params.id);

  if (!Number.isInteger(id) || id <= 0) {
    return res.status(400).json({ error: 'Invalid user id' });
  }

  res.json({ id });
});

app.param() for Reusable Logic

app.param(name, callback) runs automatically whenever a route with that parameter name matches, useful for centralizing lookups like "fetch the user for this :id once, for every route that needs it".

app.param('id', (req, res, next, id) => {
  if (!/^\d+$/.test(id)) {
    return res.status(400).json({ error: 'id must be numeric' });
  }
  req.parsedId = Number(id);
  next();
});

app.get('/users/:id', (req, res) => {
  res.json({ id: req.parsedId });
});

Common Mistakes

  • Comparing req.params.id directly to a number with ===, it is always a string.
  • Forgetting to validate that a numeric-looking parameter is actually numeric before querying a database.
  • Using the same parameter name twice in one path, only the last occurrence wins.
  • Assuming an optional parameter (:id?) will be an empty string when absent, it is actually undefined.

Key Takeaways

  • Route parameters are named URL segments prefixed with : and captured into req.params.
  • Values in req.params are always strings and should be validated/cast before use.
  • Multiple parameters can be combined in a single path for nested resources.
  • app.param() centralizes validation or lookup logic shared across many routes.

Pro Tip

For numeric IDs, validate with a strict regex like /^\d+$/ before calling Number(), this rejects malformed input like '12abc' that Number() alone would silently coerce incorrectly.