⚠️ Centralised Error Handling in Express.js: Why It Matters and How to Build It

We as developers all know how important error handling is to our projects — it allows for the smooth operation of our code. Catching and handling errors ensures our applications can run without crashing due to both client-side and server-side issues. When building an Express.js API, it can be tempting to haphazardly add try/catch blocks all over your routes. But as your codebase grows, this approach becomes very unorganised and difficult to track or maintain. That’s where centralised error handling becomes useful. With centralised error handling, we have one orderly place to manage errors across the entire application. It ensures consistency in responses, making debugging later on much simpler — both in development and in production. This post will explain the importance of centralised error handling and how to implement it in an Express.js project. The demonstration will be based on a project example: the Task Manager API developed by Earvin Tumpao. ❌ The Problem With Scattered Try/Catch Blocks At first, it might seem convenient to wrap all your Express route handlers in try/catch blocks. But doing this for every controller bloats your codebase, breaks DRY principles, and leads to repetitive logic instead of reusable patterns. A problem with consistency also arises — some controllers might send different types of error messages: some custom, some generic, and some even exposing stack traces. Even worse, important details like setting the right status code, logging the error, or hiding sensitive information in production can be easily forgotten or handled inconsistently. Here’s a typical example of a controller without centralised handling: const getUser = async (request, response) => { try { const user = await User.findById(request.params.id); if (!user) return response.status(404).json({ message: "User not found" }); response.json(user); } catch (err) { response.status(500).json({ message: err.message }); } }; It works — but imagine repeating that pattern in every controller.

Apr 16, 2025 - 11:38
 0
⚠️ Centralised Error Handling in Express.js: Why It Matters and How to Build It

We as developers all know how important error handling is to our projects — it allows for the smooth operation of our code. Catching and handling errors ensures our applications can run without crashing due to both client-side and server-side issues.

When building an Express.js API, it can be tempting to haphazardly add try/catch blocks all over your routes. But as your codebase grows, this approach becomes very unorganised and difficult to track or maintain.

That’s where centralised error handling becomes useful.

With centralised error handling, we have one orderly place to manage errors across the entire application. It ensures consistency in responses, making debugging later on much simpler — both in development and in production.

This post will explain the importance of centralised error handling and how to implement it in an Express.js project. The demonstration will be based on a project example: the Task Manager API developed by Earvin Tumpao.

❌ The Problem With Scattered Try/Catch Blocks

At first, it might seem convenient to wrap all your Express route handlers in try/catch blocks. But doing this for every controller bloats your codebase, breaks DRY principles, and leads to repetitive logic instead of reusable patterns.

A problem with consistency also arises — some controllers might send different types of error messages: some custom, some generic, and some even exposing stack traces.

Even worse, important details like setting the right status code, logging the error, or hiding sensitive information in production can be easily forgotten or handled inconsistently.

Here’s a typical example of a controller without centralised handling:

const getUser = async (request, response) => {
  try {
    const user = await User.findById(request.params.id);
    if (!user) return response.status(404).json({ message: "User not found" });

    response.json(user);
  } catch (err) {
    response.status(500).json({ message: err.message });
  }
};

It works — but imagine repeating that pattern in every controller.