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
// :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 })
})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:
// 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))
})Optional and multi-segment params
Pattern | Captures |
|---|---|
| Two required segments |
| Everything after |
| A single segment (no slashes) |
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:
// 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))params vs query vs body
Source | Example | Use for |
|---|---|---|
|
| Identifying a specific resource |
|
| Filtering, sorting, pagination |
| POST/PUT payload | Data to create or update |