Error Handling & Debugging

🌿 Node.js Lesson 14 Advanced

Robust error handling separates production-ready Node.js apps from hobby projects. This lesson covers async error propagation, custom error classes, global handlers, and debugging techniques.

1 Custom Error Classes
JavaScript — Custom Errors
// Base application error
class AppError extends Error {
  constructor(message, statusCode = 500) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true; // vs programmer errors
    Error.captureStackTrace(this, this.constructor);
  }
}

class ValidationError extends AppError {
  constructor(message) { super(message, 400); }
}

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

class UnauthorizedError extends AppError {
  constructor() { super('Authentication required', 401); }
}

// Usage in route handlers
async function getUser(req, res) {
  const user = await User.findById(req.params.id);
  if (!user) throw new NotFoundError('User');  // automatically caught
  res.json(user);
}
2 Global Error Handler & Async Wrapper
JavaScript — Error Middleware
// Async wrapper to avoid try/catch in every route
const asyncHandler = fn => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Use it on routes
app.get('/users/:id', asyncHandler(getUser));

// Global error-handling middleware (must be last!)
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') {
    res.status(statusCode).json({ error: message, stack: err.stack });
  } else {
    res.status(statusCode).json({ error: message });
  }
});

// Handle uncaught exceptions and rejections
process.on('uncaughtException', err => {
  console.error('UNCAUGHT EXCEPTION:', err);
  process.exit(1);
});

process.on('unhandledRejection', (reason) => {
  console.error('UNHANDLED REJECTION:', reason);
  process.exit(1);
});
3 Code Challenge
Challenge: Install and configure the winston logging library to write error logs to a logs/error.log file and info logs to the console. Replace all console.error() calls in your app with the logger.