Multiple Route Handlers

Pass an Array — or a Chain

Multiple Route Handlers

Routes can take multiple handler functions. Earlier ones run before later ones — a per-route middleware chain.

3 min read Level 1/5 #express#routing#handlers
What you'll learn
  • Chain handlers per route
  • Author handlers that delegate via next()
  • Use this for auth, validation, then controller

A route handler doesn’t have to be a single function. You can pass any number — Express runs them in order, each calling next() to pass control.

Comma-Separated

app.get("/admin", requireAuth, requireAdmin, listAdmins);

Express runs requireAuthrequireAdminlistAdmins. The first two are middleware; the last is the controller.

Array

const adminMiddleware = [requireAuth, requireAdmin];

app.get("/admin", adminMiddleware, listAdmins);

Arrays let you bundle middleware once and reuse it across routes.

A Validation + Controller Chain

import { z } from "zod";

function validate(schema) {
  return (req, res, next) => {
    const r = schema.safeParse(req.body);
    if (!r.success) {
      return res.status(400).json({ error: r.error.issues });
    }
    req.validBody = r.data;
    next();
  };
}

const CreateUserSchema = z.object({ name: z.string(), email: z.string().email() });

app.post("/users", validate(CreateUserSchema), (req, res) => {
  // req.validBody is typed and validated
  const user = createUser(req.validBody);
  res.status(201).json(user);
});

How next() Works

Each handler calls next() to pass to the next in the chain. If you forget — the request hangs. If you call res.send() and next(), the next handler may try to send again and throw.

The rule: send a response, OR call next(). Never both.

function requireAuth(req, res, next) {
  if (!req.headers.authorization) {
    return res.status(401).end();   // sent — return here
  }
  next();                            // OR pass to next
}

return after sending so the function exits cleanly.

Skip the Rest With next("route")

To bail out of the current route and try the next matching route:

app.get("/users/:id", (req, res, next) => {
  if (req.params.id === "me") next("route");   // try next route
  else                          next();
}, (req, res) => {
  res.json({ id: req.params.id });
});

app.get("/users/:id", (req, res) => {
  res.json({ id: "current-user-id" });
});

Rarely needed — usually a sign you should restructure.

`app.route()` Chaining →