Custom Error Handling

Error handling in Express is centralized using a special type of middleware that takes four arguments instead of three.

1. The Error Handling Middleware

You define error-handling middleware last, after other app.use() and routes calls.

app.use((err, req, res, next) => {
        console.error(err.stack);
        res.status(500).send('Something broke!');
      });

2. Triggering Errors

Just pass the error to the next() function. Express will skip all remaining non-error handlers and go straight to the error middleware.

app.get('/user/:id', async (req, res, next) => {
        try {
          const user = await User.findById(req.params.id);
          if (!user) {
            const error = new Error('User not found');
            error.status = 404;
            return next(error);
          }
          res.json(user);
        } catch (err) {
          next(err); // Handles DB errors, etc.
        }
      });

3. JSON Error Responses

In production, you want to send clean JSON errors instead of HTML stack traces.

app.use((err, req, res, next) => {
        const statusCode = err.status || 500;
        res.status(statusCode).json({
          error: {
            message: err.message,
            status: statusCode
          }
        });
      });
Pro Pattern: Create an AppError utility class that extends the standard Error class to include custom status codes and operational flags.