Error Handling in Express π‘οΈ
When an error occurs during the request-response cycle, you need a way to catch it and send a clean, helpful response to the client. Express has a default error handler, but for any real application, youβll want to create your own centralized error-handling logic.
The Default Error Handler
If an error is thrown in your application and you donβt handle it yourself, Expressβs built-in error handler will catch it. By default, it sends the errorβs stack trace to the client in an HTML page. This is useful for development but is not suitable for production because it can leak sensitive information about your serverβs structure.
Creating a Custom Error-Handling Middleware
The standard pattern in Express is to create a special βerror-handlingβ middleware. This is a centralized place to process any errors that occur in your route handlers or other middleware.
An error-handling middleware is unique because it has a different signature with four arguments instead of three:
(err, req, res, next)
err: The error object. This could be an error you created, or one thrown by a library.req,res,next: The same objects as in regular middleware.
Crucial Rule: The error-handling middleware must be defined at the very end of your middleware and route stack, after all other app.use() calls and route definitions.
How to Trigger the Error Handler
From a route handler or regular middleware, you trigger the error handler by calling next() with an argument.
next(someErrorObject)
When you call next() with an error, Express skips all subsequent regular middleware and route handlers and jumps directly to the first error-handling middleware it finds.
π» Code Example: A Centralized Error Handler
This example demonstrates a complete setup with a 404 handler and a centralized error handler.
const express = require("express");
const app = express();
// A successful route
app.get("/", (req, res) => {
res.send("Welcome!");
});
// A route that throws a synchronous error
app.get("/error-sync", (req, res) => {
throw new Error("This is a synchronous error!");
});
// A route with an asynchronous error
app.get("/error-async", (req, res, next) => {
setTimeout(() => {
// For async errors, you MUST pass the error to next().
next(new Error("This is an asynchronous error!"));
}, 1000);
});
// --- CATCH-ALL FOR 404 ERRORS ---
// This middleware runs if no route above has matched.
app.use((req, res, next) => {
res.status(404).json({ error: "Not Found" });
});
// --- CENTRALIZED ERROR HANDLER ---
// This middleware has 4 arguments, which Express recognizes as an error handler.
// It must be defined LAST.
app.use((err, req, res, next) => {
console.error(err.stack); // Log the error for debugging
// Send a generic, user-friendly error response
res.status(500).json({
error: "Something went wrong on the server.",
});
});
app.listen(3000, () => console.log("Server is running on port 3000"));Synchronous vs. Asynchronous Errors
How Express handles errors depends on whether they occur in synchronous or asynchronous code.
- Synchronous Code: If you
throw new Error()inside a synchronous route handler, Express will automatically catch it and pass it to your error-handling middleware. - Asynchronous Code (Callbacks/Promises): For errors that occur inside asynchronous code (like a database callback or a
.then()block), you must catch them and pass them tonext()manually (e.g.,next(err)). async/awaitin Express 5: A major improvement in modern Express (v5) is that errors thrown inside anasyncroute handler are now automatically caught and passed tonext(), just like synchronous errors. This greatly simplifies async error handling.
β¨ Summary
- A custom error-handling middleware is the standard pattern for centralized error logic in Express.
- It is a special middleware with four arguments:
(err, req, res, next). - It must be defined last, after all other
app.use()and route calls. - You trigger the error handler by calling
next(err)with an error object from a route or other middleware. - Remember the distinction between how synchronous and asynchronous errors are handled, and leverage the automatic handling for
async/awaitin modern Express.