Robust error handling prevents crashes and returns meaningful responses to clients.

Synchronous Errors

  try {
    JSON.parse('invalid json');
} catch (err) {
    console.error('Parse error:', err.message);
}
  

Async Errors with async/await

  async function fetchUser(id) {
    try {
        const user = await User.findById(id);
        if (!user) throw new Error('User not found');
        return user;
    } catch (err) {
        throw err; // Re-throw or transform
    }
}
  

Custom Error Classes

  class AppError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;
        this.isOperational = true;
    }
}

class NotFoundError extends AppError {
    constructor(resource) {
        super(`${resource} not found`, 404);
    }
}

// Usage
throw new NotFoundError('User');
  

Express Global Error Handler

  // Async wrapper
const catchAsync = (fn) => (req, res, next) => {
    fn(req, res, next).catch(next);
};

app.get('/users/:id', catchAsync(async (req, res) => {
    const user = await User.findById(req.params.id);
    if (!user) throw new NotFoundError('User');
    res.json(user);
}));

// Global handler (must be last middleware)
app.use((err, req, res, next) => {
    const statusCode = err.statusCode || 500;
    const message = err.isOperational ? err.message : 'Internal Server Error';

    if (process.env.NODE_ENV === 'development') {
        console.error(err.stack);
    }

    res.status(statusCode).json({
        error: message,
        ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    });
});
  

Unhandled Rejections and Exceptions

  process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection:', reason);
    // Log and gracefully shutdown
    process.exit(1);
});

process.on('uncaughtException', (err) => {
    console.error('Uncaught Exception:', err);
    process.exit(1);
});
  

404 for Unknown Routes

  app.all('*', (req, res, next) => {
    next(new NotFoundError(`Route ${req.originalUrl}`));
});
  

Validation Errors

Return 400 with details:

  if (!req.body.email) {
    throw new AppError('Email is required', 400);
}
  

Always distinguish operational errors (expected, like 404) from programming errors (bugs) — log the latter, return safe messages to clients.