Express · explained simply

How Express routes a request

A browser asks for GET /users/42. Express walks it down one list, in the order you wrote your code, until a route sends a reply or the list runs out.

GET /users/42→ app.handle→ next()→ layer.match→ dispatch→ 404
GET /users/42 req app.handle router.handle done finalhandler
1

Express hands the ticket to its router

Node's server gets the request and calls your app like a plain function. The app preps the ticket, then hands it to its router with a back-up slip.

app.listen passes the app to http.createServer, so each request runs app.handle(req, res). It sets X-Powered-By: Express and points req and res at Express's prototypes, which is where res.send comes from. Then it calls this.router.handle(req, res, done), where done is finalhandler(req, res).

ticket = req order window = app.handle back-up slip = finalhandler
/ /users/42 /users/:id GET next() stack[0] app.use stack[1] app.get
2

next() shows the ticket to each station in order

Your setup code built the line. Each app.use and app.get added one station to the end. The router starts at the first station and moves right.

const app = express()
app.use(logger)              // stack[0]
app.get('/users/:id', show)  // stack[1]
app.listen(3000)

The line is router.stack, an array of Layer objects. next() takes stack[idx++] and calls layer.match(path). stack[0] has path / and end: false, so it fits every URL. It has no route, so the router calls logger(req, res, next). When logger calls next(), the walk goes on at stack[1].

A real ticket goes to one station. Here every station that fits can work on it, then pass it on.

line = router.stack station = Layer prep station = middleware pass it on = next()

Express has no route table. It walks one list, in the order you wrote it.

/users /:id /users /42 req.params id: '42' menu GET methods
3

The next station reads its sign and fills in the blank

The sign on stack[1] says /users/:id. The ticket says /users/42. They fit, so the blank :id gets 42. Then the station checks its menu for GET.

The Layer built its matcher with path-to-regexp, using end: true and trailing: true. A hit returns { path: '/users/42', params: { id: '42' } }. Next, route._handlesMethod('GET') finds methods.get, and a HEAD request also counts as GET. The router copies the match to req.params. Then processParams runs any app.param('id', …) callback, once per value in a request.

PathFits the sign?
/users/42yes id: '42'
/users/42/yes Strict routing is off.
/USERS/42yes Matching ignores case.
/users/42/postsno end: true needs the whole path.
sign = layer path blank = req.params.id menu = route.methods
Route /users/:id get · show route.stack dispatch show() res.send()
4

The station's cook makes the dish

Inside the station is a short line of its own: one cook per handler you passed to app.get. The station gives the ticket to the first cook for GET. The cook sends the dish out, and the walk ends.

The station's own function calls route.dispatch(req, res, next). route.stack holds one Layer per handler, tagged layer.method = 'get'. dispatch picks the next layer whose method is empty or matches, and calls show(req, res, next). res.send('user 42') writes the reply, and nobody calls next again. A cook can skip the rest of the station with next('route').

cook = handler station's own line = route.stack dish = res.send()
/ …/:id stack[0] stack[1] GET end of stack 404 Cannot GET /users/42 finalhandler
5

If nobody answers, the back-up slip goes out

Say show calls next() instead, or the ticket said /teams/7. The walk reaches the end of the line. The router sends the back-up slip from step 1.

When next() runs past the last layer, it calls done(layerError). With no error, finalhandler answers 404 with the body Cannot GET /users/42. With an error, the router skips routes. It calls only layers whose function takes four arguments.

Handler doesWhat happens
res.send(…)sent The walk stops here.
next()404 finalhandler sends Cannot GET.
next(err)error Four-argument handlers run. If none replies, finalhandler uses err.status or 500.
end of the line = idx past stack.length error station = 4-argument handler

A 404 is not an error. It is the end of the line with no reply.

Before you rely on it