Working with Route Parameters
1. Defining URL Parameters
| Path | Example URL | Captures |
|---|---|---|
/users/:id | /users/42 | id="42" |
/u/:id([0-9]+) | /u/42 (numeric only) | id="42" |
/files/:name.:ext | /files/photo.jpg | name, ext |
/posts/:slug-:id | /posts/hello-42 | slug, id |
2. Accessing Parameters
Example: Reading params
app.get("/users/:id", (req, res) => {
const id = req.params.id; // string
const numericId = Number(id); // coerce
res.json({ id: numericId });
});
| Property | Type |
|---|---|
req.params | Object (always strings) |
req.params.id | String |
3. Using Multiple Parameters
Example: Nested resources
app.get("/categories/:cat/products/:id", (req, res) => {
const { cat, id } = req.params;
res.json({ category: cat, productId: id });
});
| Pattern | Result |
|---|---|
/:a/:b | Two separate path segments |
/:a-:b | Same segment, hyphen separator |
4. Validating Parameters
Example: Inline guard + express-validator
import { param, validationResult } from "express-validator";
app.get("/users/:id",
param("id").isUUID().withMessage("Invalid UUID"),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
res.json({ id: req.params.id });
}
);
| Strategy | Library |
|---|---|
| Regex in path | Built-in (:id(\\d+)) |
| Validator middleware | express-validator, zod |
| Custom guard | Plain function before handler |
5. Making Parameters Optional
Example: Optional segment
// Express 4
app.get("/users/:id?", (req, res) => {
if (req.params.id) return res.json(getUser(req.params.id));
res.json(listUsers());
});
// Express 5 — optional groups via {}
app.get("/users{/:id}", handler);
| Syntax | Express 4 | Express 5 |
|---|---|---|
| Optional | :id? | {:id} |
| Wildcard | * | {*splat} |
6. Using param() Middleware
Example: Centralized lookup
app.param("userId", async (req, res, next, id) => {
const user = await db.users.findById(id);
if (!user) return res.sendStatus(404);
req.user = user;
next();
});
app.get("/users/:userId", (req, res) => res.json(req.user));
app.put("/users/:userId", (req, res) => updateUser(req.user, req.body));
| Behavior | Detail |
|---|---|
| One per request | Cached even if param appears in multiple routes |
| Per-router | Use router.param() for scoped lookups |
7. Pre-processing Parameters
Example: Coerce + normalize
app.param("id", (req, res, next, raw) => {
const id = Number(raw);
if (!Number.isInteger(id) || id <= 0) return res.status(400).json({ error: "Invalid id" });
req.id = id; // attach typed value
next();
});
8. Converting Parameter Types
| Target | Conversion |
|---|---|
| Integer | parseInt(p, 10) or Number(p) |
| Float | parseFloat(p) |
| Boolean | p === "true" |
| Date | new Date(p) + isNaN() check |
| UUID | Validate against regex |
| Array | p.split(",") |
9. Handling Invalid Parameters
Example: 400 vs 404
app.get("/users/:id", async (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).json({ error: "id must be integer" });
const user = await db.users.findById(id);
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
});
| Status | When |
|---|---|
| 400 Bad Request | Param fails validation (wrong type/format) |
| 404 Not Found | Valid format but resource missing |
| 422 Unprocessable | Semantic validation failure |
10. Using Wildcard Parameters
Example: Catch-all
// Express 4
app.get("/files/*", (req, res) => {
const path = req.params[0]; // anything after /files/
res.sendFile(path, { root: "uploads" });
});
// Express 5 named wildcard
app.get("/files/{*splat}", (req, res) => {
const segments = req.params.splat; // array
res.json({ segments });
});
| Syntax | Captured As |
|---|---|
* (Express 4) | req.params[0] (string) |
{*splat} (Express 5) | req.params.splat (array) |
{*splat}? | Optional wildcard |
Warning: Always sanitize wildcard params used in file paths to prevent directory traversal (
../).