Express Routing
Routing in Express is mapping an incoming request — its HTTP method and URL path — to the handler that should respond. Express makes this declarative: app.get('/users', handler). This page covers the method functions, path patterns, multiple handlers per route, and the ordering rules that decide which route wins when several could match.
Method routing functions
app.get('/users', handler) // GET
app.post('/users', handler) // POST
app.put('/users/:id', handler) // PUT
app.patch('/users/:id', handler) // PATCH
app.delete('/users/:id', handler)// DELETE
app.all('/users', handler) // ANY method
app.use(handler) // every method + path (middleware)route() — group handlers for one path
When several methods share a path, app.route() avoids repeating it:
app.route('/books')
.get((req, res) => res.json(allBooks()))
.post((req, res) => res.status(201).json(create(req.body)))
app.route('/books/:id')
.get((req, res) => res.json(findBook(req.params.id)))
.put((req, res) => res.json(replace(req.params.id, req.body)))
.delete((req, res) => res.sendStatus(204))Path patterns
Pattern | Matches |
|---|---|
| Exactly |
|
|
| Multiple named params |
| Wildcard — captures the rest of the path |
| Use a RegExp route for digit-only, etc. |
The cardinal rule: order matters
// ✗ Wrong order — '/users/new' is captured by :id
app.get('/users/:id', getUser)
app.get('/users/new', showNewForm) // unreachable!
// ✓ Specific first, dynamic after
app.get('/users/new', showNewForm)
app.get('/users/:id', getUser)Multiple handlers per route
A route can take several handlers; each calls next() to pass to the next. This is how per-route middleware (auth, validation) attaches:
function requireAuth(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' })
}
next() // pass control to the next handler
}
function validate(req, res, next) {
if (!req.body.title) return res.status(422).json({ error: 'title required' })
next()
}
// Handlers run left to right; any can short-circuit by responding:
app.post('/posts', requireAuth, validate, (req, res) => {
res.status(201).json(create(req.body))
})The 404 and error fallbacks
Routes registered last act as catch-alls. A middleware with no path catches unmatched requests (404); a function with four arguments is the error handler:
// ...all your routes above...
// 404 — nothing matched (place AFTER all routes):
app.use((req, res) => {
res.status(404).json({ error: 'Not Found' })
})
// Error handler — 4 args (err, req, res, next), place LAST:
app.use((err, req, res, next) => {
console.error(err)
res.status(err.status || 500).json({ error: 'Internal Server Error' })
})Key takeaways
Use
app.METHOD(path, ...handlers); group shared paths withapp.route().Register specific/static routes before dynamic (
:param) and wildcard ones.Each handler responds or calls
next()— never both, never neither.Place the 404 catch-all and the 4-arg error handler last.
Mind Express 5 path syntax (named wildcards) if following older guides.