NodeJSRoute Parameters

Route Parameters

Route parameters are the dynamic segments of a URL path — the 42 in /users/42. You declare them with a colon (:id) and read the captured values from req.params. They're how RESTful URLs identify a specific resource, and Express makes them effortless — with a few gotchas around types, validation, and matching order.

Declaring and reading params

JS
// :id is a named parameter
app.get('/users/:id', (req, res) => {
  res.json({ userId: req.params.id })   // GET /users/42 → { "userId": "42" }
})

// Multiple parameters:
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params  // both available by name
  res.json({ userId, postId })
})
Every route parameter is a STRING
`req.params.id` is always a string — `'42'`, never the number `42`. Comparisons and arithmetic silently misbehave if you forget: `req.params.id === 42` is `false`, and `req.params.id + 1` is `'421'` (string concat). Convert explicitly with `Number(req.params.id)`, and validate the result (a non-numeric id yields `NaN`).

JS
app.get('/products/:id', (req, res) => {
  const id = Number(req.params.id)
  if (!Number.isInteger(id) || id < 1) {
    return res.status(400).json({ error: 'Invalid id' })
  }
  const product = findProduct(id)
  if (!product) return res.status(404).json({ error: 'Not found' })
  res.json(product)
})
Constraining params to a format

To accept only certain shapes (digits only, a UUID), validate inside the handler, or use a RegExp route. Validating in the handler is the most portable approach across Express versions:

JS
// Reject non-numeric ids early (works in any Express version):
app.get('/orders/:id', (req, res, next) => {
  if (!/^\d+$/.test(req.params.id)) return next()   // fall through to 404
  res.json(getOrder(req.params.id))
})
Falling through with `next()` vs responding 400
Two valid strategies for a malformed param: respond `400 Bad Request` immediately (the client clearly sent garbage), or call `next()` to let it fall through to your 404 handler (treating `/orders/abc` as "no such route"). Pick based on whether a bad format means "invalid request" or "not a real URL" in your API's design — and be consistent.
Optional and multi-segment params

Pattern

Captures

/posts/:year/:month

Two required segments

/files/*splat

Everything after /files/ (named wildcard, v5)

/search/:term

A single segment (no slashes)

A `:param` matches ONE segment — it stops at `/`
`:id` captures characters up to the next slash, so `/files/:name` matches `/files/report` but **not** `/files/2024/report` (two segments). To capture a path with slashes (like a nested file path), use a named wildcard `/files/*splat` — in Express 5 the wildcard must be named. Then read `req.params.splat` for the full remainder.
app.param() — run logic when a param appears

app.param('name', fn) registers a handler that runs whenever that parameter is present in a matched route — perfect for loading a resource once and attaching it to req, instead of repeating the lookup in every handler:

JS
// Runs before any route with :userId — load the user once:
app.param('userId', async (req, res, next, id) => {
  const user = await db.users.find(Number(id))
  if (!user) return res.status(404).json({ error: 'User not found' })
  req.user = user                       // attach for downstream handlers
  next()
})

// Now handlers can rely on req.user being present:
app.get('/users/:userId', (req, res) => res.json(req.user))
app.get('/users/:userId/settings', (req, res) => res.json(req.user.settings))
`app.param` centralizes resource loading and 404s
Without it, every `/users/:userId/...` route repeats "find the user, 404 if missing." `app.param` runs that logic in one place and stashes the result on `req`, keeping handlers focused. It's the Express idiom for "resolve this id to an object before the route runs."
params vs query vs body

Source

Example

Use for

req.params

/users/:id/users/42

Identifying a specific resource

req.query

?sort=name&page=2

Filtering, sorting, pagination

req.body

POST/PUT payload

Data to create or update

Choose the right channel for each kind of input
A clean API uses each appropriately: the resource identity goes in the **path** (`/users/42`), optional modifiers go in the [**query string**](/nodejs/query-parameters) (`?fields=name`), and the payload goes in the **body**. Cramming everything into one (e.g. an id in the query, or a filter in the path) makes URLs confusing and caching harder.
Next
Read the optional modifiers from the URL's query string: [Query Parameters](/nodejs/query-parameters).