Run Logic Every Time a Param Appears
`app.param()`
app.param(name, fn) wires a handler that runs whenever a route has the named param. Great for fetch-and-validate.
What you'll learn
- Register a param handler
- Replace boilerplate "find by id" code
- Know when this trick shines vs hurts
app.param(name, fn) registers a function that runs before any
route with a :name parameter. Use it to validate or hydrate.
Without It — Repetition
app.get("/users/:id", async (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).end();
const user = await db.users.findById(id);
if (!user) return res.status(404).end();
res.json(user);
});
app.patch("/users/:id", async (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).end();
const user = await db.users.findById(id);
if (!user) return res.status(404).end();
// ... update
}); The same validate-and-fetch dance, repeated in every handler.
With It — DRY
app.param("id", async (req, res, next, value) => {
const id = Number(value);
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" });
}
req.user = user;
next();
});
app.get("/users/:id", (req, res) => {
res.json(req.user); // already loaded
});
app.patch("/users/:id", async (req, res) => {
Object.assign(req.user, req.body);
await db.users.save(req.user);
res.json(req.user);
}); On a Router
Router#param works the same way:
const userRouter = Router();
userRouter.param("id", loadUser);
userRouter.get("/:id", getUser);
userRouter.patch("/:id", updateUser);
app.use("/api/users", userRouter); The param handler is scoped to that router — it doesn’t fire on
other routes that happen to use :id.
Caveats
- Param handlers only fire when the parameter is present in the matched route.
- They fire once per request, even if the route has the param multiple times.
- Async errors must call
next(err)(or throw, in Express 5).
When To Use
app.param is a sharp tool. Good for:
- “Load this resource by ID” patterns
- Validation that applies across many routes
Bad for:
- Logic that should be visible at each call site
- Routes where the param means different things
Used sparingly, it removes a lot of boilerplate. Overused, it hides what a route does.
Regex Routing →