npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

deep-express-errors

v1.9.3

Published

Centralized typed error management library for ExpressJS with Zod/Joi/JWT/express-validator and DB (Prisma/Mongoose/Sequelize) support & graceful shutdown

Readme

Deep Express Errors

Deep Express Errors is a library for standardizing error handling in Node.js applications built with Express. It provides ready-to-use error classes, a centralized error handler middleware, automatic database error mapping, validation error mapping, JWT handling, and built-in logging or custom logger support.


Features

  • Ready-to-use HTTP presets: BadRequest, NotFound, Unauthorized, and others.
  • Centralized handling: one middleware catches all errors and formats them into a unified JSON response.
  • Automatic mapping: converts native errors like JWT, MongoDB duplicate key errors, Prisma, Sequelize, Zod, Joi, and express-validator errors into clear HTTP responses.
  • Logging: built-in logger with levels and timestamps.
  • Custom logger: integrate Winston, Pino, or another logger by passing it into the configuration.
  • Security: in production, stack traces and sensitive data are hidden.
  • Fully customizable response: adapt the error structure to match your API standards.
  • Global handlers: optional handling of uncaughtException and unhandledRejection with graceful shutdown support.
  • TypeScript support: includes .d.ts files for full typing support.

Installation

npm install deep-express-errors

Integration

Add errorHandler at the end of your Express middleware chain.

const { errorHandler } = require('deep-express-errors');

Example:

const express = require('express');
const { errorHandler } = require('deep-express-errors');

const app = express();

// ... your routes ...

// Error handler MUST be after all routes
app.use(errorHandler);

app.listen(3000, () => console.log('Server running...'));

Warning If you use only errorHandler, the library performs duck-typing checks.

Want strict checks? Visit the configuration section and use the errorClasses option.


Usage

Throwing Errors with Presets

const { Errors } = require('deep-express-errors');

app.get('/users/:id', async (req, res, next) => {
  const user = await getUserById(req.params.id);

  if (!user) {
    return next(Errors.NotFound('User not found'));
  }

  if (!user.isActive) {
    return next(Errors.Forbidden('Access denied'));
  }

  res.json(user);
});

Async Function Wrapper

const { Errors, asyncHandler } = require('deep-express-errors');

const getUser = asyncHandler(async (req, res, next) => {
  const data = await database.query();
  if (!data) return next(Errors.BadRequest('No data'));
  res.json(data);
});

app.get('/data', getUser);

Custom Errors with AppError

const { AppError } = require('deep-express-errors');

throw new AppError('Custom payment gateway error', 402, true);

Graceful Shutdown and Global Handlers

You can enable handling of global errors (uncaughtException, unhandledRejection) and termination signals (SIGINT, SIGTERM) so the app can log the crash and perform cleanup before exiting.

initGlobalHandlers(options)

| Option | Type | Default | Description | | :--- | :--- | :--- | :--- | | closeServer | Function | undefined | Async function to close your HTTP server. | | onShutdown | Function | undefined | Cleanup logic during normal exit. | | onCrash | Function | undefined | Cleanup logic during uncaughtException or unhandledRejection. | | exitOnUnhandledRejection | Boolean | true | Exit process after rejection. | | exitOnUncaughtException | Boolean | true | Exit process after exception. | | maxTimeout | number | 10000 | Timeout in milliseconds for awaited cleanup. |

gracefulHttpClose(server)

A helper that wraps server.close() into a Promise with support for an abort signal.

Example usage

const { initGlobalHandlers, gracefulHttpClose } = require('deep-express-errors');

initGlobalHandlers({
  closeServer: gracefulHttpClose(server),
  onShutdown: async () => {
    console.log('Cleaning up...');
    await mongoose.disconnect();
  },
  onCrash: async (err) => {
    await sendAlertToAdmin(err);
  }
});

Available Error Presets

All methods are available via the Errors object. Default isOperational is true.

| Method | Status Code | Description | |--------|-------------|-------------| | Errors.BadRequest(message) | 400 | Bad Request | | Errors.Unauthorized(message) | 401 | Unauthorized | | Errors.PaymentRequired(message) | 402 | Payment Required | | Errors.Forbidden(message) | 403 | Forbidden | | Errors.NotFound(message) | 404 | Not Found | | Errors.Conflict(message) | 409 | Conflict | | Errors.UnprocessableContent(message) | 422 | Unprocessable Content | | Errors.TooManyRequests(message) | 429 | Too Many Requests | | Errors.InternalServerError(message) | 500 | Internal Server Error | | Errors.NotImplemented(message) | 501 | Not Implemented | | Errors.BadGateway(message) | 502 | Bad Gateway | | Errors.ServiceUnavailable(message) | 503 | Service Unavailable | | Errors.GatewayTimeout(message) | 504 | Gateway Timeout |


Configuration and Environment Variables

  • NODE_ENV:

    • development - stack trace included in response
    • production or any other value - stack trace hidden, only message and status returned
  • DEBUG=true - outputs extra debug info about error mapping.

You can customize the response format and environment behavior with setConfig.

Important customMappers must be synchronous. Async functions and Promises are ignored.

const { setConfig, errorHandler } = require('deep-express-errors');
const logger = require('./utils/logger');
const z = require('zod');
const Joi = require('joi');
const { Sequelize } = require('sequelize');
const { Prisma } = require('@prisma/client');

setConfig({
  customLogger: logger,
  errorClasses: {
    Zod: z,
    Joi: Joi,
    Prisma,
    Sequelize
  },
  needMappers: ['zod', 'joi', 'prisma'],
  maxLoggerRequests: 1000,
  customMappers: [
    (err) => {
      if (err.name === 'newError') {
        return Errors.BadRequest();
      }
    }
  ],
  devEnvironments: ['development', 'dev'],
  formatError: (err, { req, isDev }) => {
    return {
      success: false,
      error: {
        code: err.statusCode,
        message: err.message,
        ...(isDev ? { debug_stack: err.stack } : {})
      }
    };
  }
});

const app = express();
app.use(errorHandler);

Library Logger

By default, if customLogger is not set, the library uses its own logger.

| Logger | Params | Output color | |--------|--------|--------------| | logError | error, req (optional) | red | | logWarning | message, req (optional) | yellow | | logInfo | message | default white | | logDebug | message, req (optional) | blue |

Custom Logger

const { setConfig } = require('deep-express-errors');
const winston = require('winston');

const logger = winston.createLogger({
  // your config
});

setConfig({
  customLogger: logger
});

Default Response Format

{
  "status": "error",
  "method": "GET",
  "url": "/api/resource",
  "message": "Error description",
  "stack": "..."
}

Default Config Format

let config = {
  customMappers: [],
  customLogger: null,
  errorClasses: null,
  needMappers: null,
  maxLoggerRequests: 100,
  devEnvironments: ['dev', 'development'],
  formatError: (err, { req, isDev }) => ({
    status: err.isOperational ? 'fail' : 'error',
    message: err.message,
    ...(isDev ? {
      method: req.method,
      url: req.originalUrl,
      stack: err.stack
    } : {})
  })
}

Third-Party Error Mapping

mapErrorNameToPreset maps non-AppError instances to HTTP responses.

Supported mappings:

  • JWT: JsonWebTokenError, TokenExpiredError, NotBeforeError -> 401 Unauthorized
  • express-validator: FieldValidationError, GroupedAlternativeValidationError, AlternativeValidationError -> 422 Unprocessable Content
  • Validation libraries: ZodError, ValidationError from Joi
  • Mongoose / MongoDB: CastError, duplicate key errors, validation errors, and MongoServerError
  • Prisma: PrismaClientKnownRequestError, PrismaClientUnknownRequestError, PrismaClientRustPanicError, PrismaClientInitializationError, PrismaClientValidationError
  • Sequelize: SequelizeUniqueConstraintError, SequelizeValidationError, SequelizeForeignKeyConstraintError, SequelizeOptimisticLockError, SequelizeEmptyResultError, SequelizeDatabaseError, SequelizeConnectionError, SequelizeTimeoutError, SequelizeConnectionRefusedError, SequelizeHostNotFoundError, SequelizeHostNotReachableError, SequelizeAccessDeniedError
  • JavaScript native errors: ReferenceError, TypeError, and SyntaxError

Example Client Response

Development mode

{
  "status": "error",
  "method": "GET",
  "url": "/api/users/999",
  "message": "User not found",
  "stack": "Error: User not found\n    at /app/controllers/user.js:15:20..."
}

Production mode

{
  "status": "error",
  "message": "User not found"
}