Loading

Quipoin Menu

Learn • Practice • Grow

express-js / Express.js Error Handling
tutorial

Express.js Error Handling

No matter how carefully you write code, errors will happen. Network issues, invalid user input, database connection problems – your Express app needs to handle these gracefully. Proper error handling and debugging practices separate professional applications from amateur ones.

Types of Errors in Express

Error TypeExample
Operational ErrorsInvalid input, database timeout, file not found
Programming ErrorsBug in code, undefined variable, null reference
System ErrorsOut of memory, disk full, network down

Error handling is like airbags in a car – you hope you never need them, but you're glad they're there when something goes wrong.

Basic Error Handling Patterns

1. Try/Catch with Async/Await
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
   
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
   
    res.json(user);
  } catch (err) {
    <!-- Pass error to Express error handler -->
    next(err);
  }
});

2. Wrapper Function to Avoid Try/Catch Repetition
const catchAsync = (fn) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

<!-- Use it like this -->
app.get('/users/:id', catchAsync(async (req, res) => {
  const user = await User.findById(req.params.id);
 
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
 
  res.json(user);
}));

Error Handling Middleware

Express has special middleware for errors – it takes four parameters `(err, req, res, next)`. Place it at the end of your middleware chain.
<!-- Custom error class -->
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

<!-- Error handling middleware -->
app.use((err, req, res, next) => {
  <!-- Default values -->
  err.statusCode = err.statusCode || 500;
  err.message = err.message || 'Internal Server Error';
 
  <!-- Log error for debugging (but not in production?) -->
  console.error('ERROR 💥:', err);
 
  <!-- Send different responses in development vs production -->
  if (process.env.NODE_ENV === 'development') {
    res.status(err.statusCode).json({
      error: err.message,
      stack: err.stack,
      isOperational: err.isOperational
    });
  } else {
    <!-- Production: don't leak error details -->
    if (err.isOperational) {
      res.status(err.statusCode).json({ error: err.message });
    } else {
      <!-- Programming or unknown error -->
      res.status(500).json({ error: 'Something went wrong' });
    }
  }
});

404 Handler

This goes before your error handler but after all routes.
app.use((req, res, next) => {
  return next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});

Debugging Techniques

1. Console.log (Basic but Effective)
console.log('Request body:', req.body);
console.log('User from DB:', user);

2. Debug Module
npm install debug
const debug = require('debug')('app:server');

app.listen(3000, () => {
  debug('Server running on port 3000');
});

<!-- Run with: DEBUG=app:* node app.js -->

3. Using Node.js Inspector
node --inspect app.js
<!-- Then open chrome://inspect in Chrome -->

4. VS Code Debugger
Create a `.vscode/launch.json` configuration:
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": ["/**"],
      "program": "${workspaceFolder}/app.js"
    }
  ]
}

Best Practices Summary

  1. Use try/catch with async/await or wrap async functions.
  2. Create a custom error class to distinguish operational errors.
  3. Always pass errors to Express with `next(err)`.
  4. Have a centralized error handler at the end of your middleware.
  5. Don't leak error details in production.
  6. Log errors for debugging and monitoring.
  7. Handle 404 errors before your main error handler.

Two Minute Drill

  • Use try/catch with async/await and pass errors to `next()`.
  • Create a wrapper like `catchAsync` to avoid repetitive try/catch.
  • Error-handling middleware has 4 parameters `(err, req, res, next)`.
  • Distinguish between operational and programming errors.
  • Use debugging tools: console.log, debug module, Node.js inspector, VS Code debugger.

Need more clarification?

Drop us an email at career@quipoinfotech.com