Global Error Handling

A global error handler is a core part of any production-ready Express application.
It ensures that all errors—expected or unexpected—are handled consistently, securely, and predictably.

Servercn provides a structured approach using:

  • ApiError for operational errors
  • ApiResponse for consistent responses
  • STATUS_CODES for standardized HTTP status codes
  • Express error middleware for central handling

Installation Guide

npx servercn-cli add global-error-handler

Why a Global Error Handler?

Without a global error handler:

  • Errors leak stack traces to clients
  • Response formats become inconsistent
  • Debugging and logging are fragmented
  • Async errors may crash the process

With a global error handler:

  • One place to handle all errors
  • Consistent API response shape
  • Clear separation of operational vs programmer errors
  • Easier logging and monitoring

Error Flow in Servercn

  • Route or service throws an error (ApiError or native error)
  • Error bubbles through AsyncHandler
  • Express forwards it to the global error middleware
  • The middleware formats and sends a standardized response

Prerequisites

Ensure the following environment variables are defined:

PORT="3000"
NODE_ENV="development"
LOG_LEVEL="info"

Ensure the following configuration are defined:

src/configs/env.ts
interface Config {
  PORT: number;
  NODE_ENV: string;
  LOG_LEVEL: string;
}
 
const env: Config = {
  PORT: Number(process.env.PORT) || 3000,
  NODE_ENV: process.env.NODE_ENV || "development",
  LOG_LEVEL: process.env.LOG_LEVEL || "info"
};
 
export default env;

Basic Implementation

src/middlewares/error-handler.ts
import { Request, Response, NextFunction } from "express";
import env from "../configs/env";
 
import { ApiError } from "../utils/api-error";
import { logger } from "../utils/logger";
import { STATUS_CODES } from "../constants/status-codes";
 
export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  let statusCode = 500;
  let message = "Internal server error";
 
  if (err instanceof ApiError) {
    statusCode = err.statusCode;
    message = err.message;
  }
 
  logger.error(
    err,
    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`
  );
 
  const response = {
    success: false,
    message,
    ...(env.NODE_ENV === "development" && { stack: err.stack })
  };
 
  res.status(statusCode).json(response);
};
src/shared/middlewares/error-handler.ts
import { Request, Response, NextFunction } from "express";
import env from "../configs/env";
 
import { ApiError } from "../errors/api-error";
import { logger } from "../utils/logger";
import { STATUS_CODES } from "../constants/status-codes";
 
export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  let statusCode = 500;
  let message = "Internal server error";
 
  if (err instanceof ApiError) {
    statusCode = err.statusCode;
    message = err.message;
  }
 
  logger.error(
    err,
    `Error: ${message} | Status: ${statusCode} | Path: ${req.method} ${req.originalUrl}`
  );
 
  const response = {
    success: false,
    message,
    ...(env.NODE_ENV === "development" && { stack: err.stack })
  };
 
  res.status(statusCode).json(response);
};

Usage Example

src/app.ts
import express, { type Application } from "express";
import "dotenv-flow/config";
import { errorHandler } from "./middlewares/error-handler";
 
const app: Application = express();
 
app.use(express.json());
 
// routes here
// ....
 
// Global error handler (should be after routes)
app.use(errorHandler);
 
export default app;

File & Folder Structure

Loading files...

Installation

npx servercn-cli add global-error-handler