How to handle errors in Express

Proper error handling is crucial for building robust Express applications that gracefully handle failures and provide meaningful responses to clients. As the creator of CoreUI with over 25 years of backend development experience, I’ve implemented error handling systems across numerous production APIs. The most effective approach is using Express’s built-in error handling middleware combined with custom error classes for different error types. This ensures consistent error responses and proper logging while preventing application crashes.

Use Express error handling middleware with custom error classes to catch and process all application errors consistently.

const express = require('express')
const app = express()

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

// Route that might throw an error
app.get('/users/:id', async (req, res, next) => {
  try {
    const userId = req.params.id

    if (!userId) {
      throw new AppError('User ID is required', 400)
    }

    // Simulate database call that might fail
    const user = await getUserById(userId)

    if (!user) {
      throw new AppError('User not found', 404)
    }

    res.json(user)
  } catch (error) {
    next(error) // Pass error to error handler
  }
})

// Global error handling middleware (must be last)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500
  const message = err.isOperational ? err.message : 'Something went wrong!'

  console.error('Error:', err)

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

This error handling setup uses a custom AppError class to distinguish between operational errors (client mistakes) and programming errors. The route handler catches errors and passes them to the error middleware using next(error). The global error middleware provides consistent error responses with appropriate status codes and includes stack traces only in development.

Best Practice Note:

This error handling pattern is used throughout CoreUI’s Node.js templates for reliable, production-ready APIs. Always use try-catch blocks for async operations, create specific error types for different scenarios, and log errors for monitoring while sending safe messages to clients.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to force a React component to re-render
How to force a React component to re-render

How to Detect a Click Outside of a React Component
How to Detect a Click Outside of a React Component

The Best Bootstrap Alternative for Developers in 2025
The Best Bootstrap Alternative for Developers in 2025

How to loop through a 2D array in JavaScript
How to loop through a 2D array in JavaScript

Answers by CoreUI Core Team