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 🙏

© 2025 – Pkg Stats / Ryan Hefner

ds-express-errors

v1.4.1

Published

Centralized typed error handling library for ExpressJS with Zod/Joi/JWT and DB (Prisma/Mongoose/Sequelize) support

Readme

📦 DS Express Errors

DS Express Errors is library for standardizing error handling in Node.js applications built with Express.
It provides ready-to-use error classes (HTTP Presets), a centralized error handler (middleware), automatic: database error mapping (Mongoose, Prisma, Sequelize), validation error mapping (Zod, Joi), JWT and built-in simple logging or custom loggers (Winston/Pino).


Official website & detailed documentation with examples: ds-express-errors


✨ Features

  • Ready-to-use HTTP presets: BadRequest, NotFound, Unauthorized, and others, corresponding to standard HTTP codes.
  • 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 or Prisma/Sequelize/Zod/Joi validation errors) into clear HTTP responses.
  • Logging: Built-in logger with levels (Error, Warning, Info, Debug) and timestamps.
  • Custom Logger: Easily integrate external loggers like Winston or Pino by passing them into the configuration.
  • Security: In production (NODE_ENV=production), stack traces, sensitive data are hidden; visible in development.
  • Fully Customizable Response: Adapt the error structure to match your API standards (JSON:API, legacy wrappers, etc.).
  • Global Handlers: Optional handling of uncaughtException and unhandledRejection with support for Graceful Shutdown (custom cleanup logic).
  • TypeScript support: Includes .d.ts files for full typing support.

🚀 Installation

npm install ds-express-errors

🛠 Integration

Add errorHandler at the end of your Express middleware chain.

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

const app = express();

// ... your routes ...

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

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

📖 Usage

1. Throwing Errors (Using Presets)

No need to remember status codes. Just import Errors and use the method you need.

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

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

    if (!user) {
        // Automatically sends 404 with message "User not found"
        return next(Errors.NotFound('User not found'));
    }

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

    res.json(user);
});

2. Using AppError (Custom Errors)

Create specific errors using the AppError class:

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

// (message, statusCode, isOperational)
throw new AppError('Custom payment gateway error', 402, true);

3. Async Function Wrapper (asyncHandler)

Avoid repetitive try/catch in every controller.

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

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

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

4. Global Process Handlers (Graceful Shutdown)

You can explicitly enable handling of global errors (uncaughtException, unhandledRejection). This allows you to log the crash and perform cleanup (like closing server connections) before exiting.

Basic Usage: Logs the error and exits (process.exit(1)).

const { initGlobalHandlers } = require('ds-express-errors');

// Initialize at the entry point of your app
initGlobalHandlers();

Advanced Usage (Custom Crash Logic): UYou can provide an onCrash callback to perform cleanup tasks (e.g., closing DB connections, sending alerts) before the process exits.

New in v1.3.0:

  • The callback receives the error object that caused the crash.
  • Supports async/await. The library waits for your promise to resolve (up to 10s) before exiting.
  • No need to call process.exit(1) manually — the library does it for you automatically after your callback finishes.
const { initGlobalHandlers } = require('ds-express-errors');

initGlobalHandlers({
  // Optional: Prevent exit on unhandledRejection (default: true)
  exitOnUnhandledRejection: true,

  // Async callback with error access
  onCrash: async (err) => {
    console.error('CRASH DETECTED:', err.message); // Access the error!

    // Send alert to Sentry/Telegram
    await sendAlertToAdmin(err);

    // Close resources
    await db.disconnect();
    console.log('Cleanup finished.');

    // The library will automatically execute process.exit(1) after this function
  }
});

📋 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.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 |


⚙️ Configuration & Environment Variables

  • NODE_ENV:

    • development — stack trace included in response
    • production (or any other) — stack trace hidden, only message and status returned

    You can define your own dev environment name using setConfig

⚙️ Configuration

  • DEBUG=true — outputs extra debug info about error mapping (mapErrorNameToPreset)

You can customize the structure of the error response sent to the client. This is useful if you need to adhere to a specific API standard (e.g., JSON:API) or hide certain fields.

Also you can customize dev environment by using devEnvironments: []

Use setConfig before initializing the error handler middleware.

const { setConfig, errorHandler } = require('ds-express-errors');
const logger = require('./utils/logger'); // Your Winston/Pino logger

// Optional: Customize response format and Logger
setConfig({
    customLogger: logger, 
    
    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();
// ... your routes ...
app.use(errorHandler);

🔌 Custom Logger (New in v1.4.0)

You can connect your own logger (like Winston, Pino) instead of the built-in console logger. The object must support 4 methods: error, warn, info, debug.

const { setConfig } = require('ds-express-errors');
const winston = require('winston'); // Example

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

// Pass your logger instance
setConfig({
    customLogger: logger
});

**Default Response Format**

If no config is provided, the library uses the default format:

```json
{
  "status": "error", // or 'fail'
  "method": "GET", // showed when NODE_ENV= development or dev
  "url": "/api/resource", // showed when NODE_ENV= development or dev
  "message": "Error description",
  "stack": // showed when NODE_ENV= development or dev
}

Default Config Format

let config = {
    customMappers: [],
    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 automatically maps non-AppError instances (e.g., database errors) to HTTP responses.

Supported mappings:

  • JWT: JsonWebTokenError, TokenExpiredError, NotBeforeError → mapped to 401 Unauthorized
  • Validation Libraries: ZodError (Zod), ValidationError (Joi) — automatically formatted into readable messages.
  • Mongoose / MongoDB: CastError, DuplicateKeyError (code 11000), ValidationError, MongoServerError is handled (400 for bad JSON body, 500 for code errors).
  • Prisma: PrismaClientKnownRequestError, PrismaClientUnknownRequestError, PrismaClientRustPanicError, PrismaClientInitializationError, PrismaClientValidationError
  • Sequelize: SequelizeUniqueConstraintError, SequelizeValidationError, SequelizeForeignKeyConstraintError
  • JS Native: ReferenceError, TypeError → mapped to 500. SyntaxError is handled (400 for bad JSON body, 500 for code errors).

📝 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"
}