A Catch-All When Nothing Else Matches
404 Handlers
Add a catch-all route last. It runs only when no earlier route matched — a clean place to return 404.
What you'll learn
- Add a final fallback
- Differentiate API vs page 404s
- Place it correctly relative to error middleware
When no route matches a request, Express’s default response is a plain-text “Cannot GET /…”. Replace that with your own 404 handler mounted last.
The Catch-All
app.get("/users", listUsers);
app.post("/users", createUser);
// ... more routes
app.use((req, res) => {
res.status(404).json({ error: "not found", path: req.path });
}); Mount after all routes. Express only reaches it when nothing else handled the request.
API vs Page 404s
Split the 404 by accept type:
app.use((req, res) => {
if (req.accepts("html")) {
return res.status(404).sendFile(path.resolve("public/404.html"));
}
res.status(404).json({ error: "not found" });
}); API clients get JSON; browsers get a styled HTML page.
With Error Middleware
The order matters: 404 catch-all, then the error middleware.
// routes
app.use(routes);
// 404
app.use((req, res) => {
res.status(404).json({ error: "not found" });
});
// errors (must be 4-arg)
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "internal" });
}); Be Careful With app.use vs app.all
app.all("*splat", handler) works too, but app.use(handler) is
simpler and doesn’t require a path pattern. Either is fine.
Don’t 404 Twice
Some bad patterns put the 404 handler inside individual route handlers AND globally. Pick one place — usually global.
End of Chapter
Next chapter: middleware — the request pipeline that powers everything.
Middleware Intro →