Error Handling & Status Codes
Handle failures gracefully and return clear, correct responses to the front-end.
What you will learn
- Use proper HTTP status codes
- Add an error-handling middleware
- Validate input
Why status codes matter
When something goes wrong, the front-end needs to know what went wrong — and the polite way to say so is an HTTP status code. A number in the 200s means success, 400s means "you (the caller) made a mistake", and 500s means "the server hit a problem". Sending the right code lets the front-end react correctly (show a "not found" page, a validation message, and so on).
An error-handling middleware
Two safety nets catch the common failures. Here is the order they fire in:
- A request comes in and Express tries to match it to one of your routes.
- If no route matches the URL, the catch-all
404middleware runs and replies "Route not found". - If a route runs but throws an error (or calls
next(err)), Express skips to the special error handler. - The central error handler logs the problem and replies with
500so the client gets a clean message instead of a crash.
Express knows a middleware is the error handler because it has four arguments (err, req, res, next). Both of these go at the bottom, after all your routes:
// ... your routes ...
// 404 for unmatched routes
app.use((req, res) => {
res.status(404).json({ error: "Route not found" });
});
// Central error handler (must have 4 args, and go last)
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Something went wrong" });
});Note: Output (requesting a URL with no matching route):
{"error":"Route not found"}
Output (when a route throws an error):
{"error":"Something went wrong"}
The first app.use has the usual two arguments, so it acts as a normal middleware — placed last, it only runs when nothing else matched, making it a perfect 404 catch-all. The second has four arguments starting with err, which is how Express recognises the error handler; console.error(err) records the real details for you, while the caller just gets a tidy 500 message (never leak raw errors to users).
Validate input
Before acting on data a client sent, check it is valid. If a required field is missing, reject the request early with status 400 (Bad Request) and a helpful message — do not let bad data into your system:
app.post("/tasks", (req, res) => {
if (!req.body.text) {
return res.status(400).json({ error: "text is required" });
}
// ... create the task ...
});Note: Output (POST /tasks with an empty or missing text):
{"error":"text is required"}
if (!req.body.text) is true when text is missing or empty. In that case we return immediately with a 400 — the return is important because it stops the function before it tries to create the task. Only valid requests get past this check.
Watch out: Never trust input from the front-end. Always validate on the server — the browser’s validation can be bypassed (you learned this in the HTML forms lesson).
Q. How many arguments does an Express error-handling middleware take?
✍️ Practice
- Add a catch-all 404 handler and a central error handler to an API.
- Validate that a required field is present and return 400 if missing.
🏠 Homework
- Add validation and proper status codes to your CRUD API.