`app.param()`

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.

3 min read Level 2/5 #express#routing#params
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 →