close
Skip to content

Repository files navigation

📦 DS Express Errors NPM Downloads

DS Express Errors is a 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, express-validator), 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, express-validator) 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 { errorHandler } = require('ds-express-errors');

If you need examples in ESM not commonjs please visit ds-express-errors

Warning

Good to know

If you use only errorHandler the library would perform only duck-typing checks.

Tip

Want strict checks?

Visit the Configuration section. There you will find the errorClasses config property.

Example:

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...'));

Important

About Environment and Prod vs Dev Responses

By default, the library relies on the NODE_ENV environment variable to format error responses. To see how Production and Development responses differ, visit the Configuration See the devEnvironments property to customize this behavior.


📖 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. 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) return next(Errors.BadRequest('No data'));
    res.json(data);
});

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

3. 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);

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

Tip

More detailed information

Visit the Global Handlers and Graceful Shutdown.

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

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

Graceful Shutdown & Global Handlers (v1.8.0+)

DS Express Errors provides a robust way to handle application crashes and termination signals (SIGINT, SIGTERM). It ensures your server stops accepting new connections and finishes active requests before exiting.

initGlobalHandlers(options)

Option Type Default Description
closeServer Function undefined Async function to close your HTTP server.
onShutdown Function undefined Cleanup logic (e.g., disconnect DB) 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 awaited with a N-second timeout. The library calls process.exit(1) after it completes, so you no longer need to exit manually.

gracefulHttpClose(server)

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

Example usage

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

initGlobalHandlers({
  closeServer: gracefulHttpClose(server), // Gracefully close server
  onShutdown: async (signal) => {
    console.log('Cleaning up...');
    await mongoose.disconnect(); // Close DB connections
  },
  onCrash: async (err, signal) => {
    await sendAlertToAdmin(err); // Notify dev team about crash
  }
});

Important

Be aware

initGlobalHandlers can only be called once. Calling it again will throw a GlobalHandlerAlreadySet error.

GlobalHandlerAlreadySet is an internal ds-express-errors error with the code ERR_DS_EXPRESS_ERRORS_GLOBAL_HANDLER_ALREADY_SET.


📋 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

Tip

More detailed information

Visit the API REFERENCES.


⚙️ 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

Important

About Environment and Prod vs Dev Responses

If NODE_ENV is not defined, you will receive sanitised error messages, as the library behaves as if the environment were set to production. To see how Production and Development responses differ, visit the Configuration See the devEnvironments property to customize which environments should display full error information (stack traces and full error messages).

⚙️ 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.

Important

Be aware

setConfig can only be called once. Calling it again will throw a ConfigAlreadySet error.

ConfigAlreadySet is an internal ds-express-errors error with the code ERR_DS_EXPRESS_ERRORS_CONFIG_ALREADY_SET.

Important

customMappers must be synchronous. Async function or Promise are not supported and will be ignored.

const { setConfig, errorHandler } = require('ds-express-errors');
const logger = require('./utils/logger'); // Your Winston/Pino logger
const z = require('zod');
const Joi = require('joi');
const { Sequelize } = require('sequelize');

const { PrismaClient, Prisma  } = require('@prisma/client');
// Optional: Customize response format and Logger
setConfig({
    // (OPTIONAL)
    customLogger: logger, 

    // From version v1.8.0+
    // (OPTIONAL) You can replace default ds-express-errors check (duck-typing) to more strict by passing error class

    // For now is available only Zod, Joi, Sequelize, Prisma
    errorClasses: {
      Zod: z,
      Joi: Joi,
      Prisma,
      Sequelize
    },

    // (OPTIONAL) By default  ds-express-errors use all available mapper, but from v1.8.0+ you can choose only needed mappers
    // Mappers ['zod', 'joi', 'mongoose', 'prisma', 'sequelize', 'expressValidator']
    needMappers: ['zod', 'joi', 'prisma'], // (In this example) For now library would map only ['zod', 'joi', 'prisma'] errors, other would be `InternalServerError` or if is specified `customMappers` it would use that response

    // ----
    
    // (OPTIONAL) Set preferred log rate per 1 minute
    maxLoggerRequests: 1000,

    // (OPTIONAL) Define your custom mappers and ds-express-errors would use them first
    customMappers: [
        (err) => {
            if (err.name === 'newError') {
                return Errors.BadRequest()
            }
        }
        // ...
    ],
    // (OPTIONAL)
    devEnvironments: ['development', 'dev'],

    // (OPTIONAL)
    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);

🔌 Library logger

By default if you not set customLogger in setConfig library used his own logger

Library logger have rate logging limits support

Loggers params Output color
logError error, req (optional) red
logWarning message, req (optional) yellow
logInfo message default white
logDebug message, req (optional) blue

🔌 Custom Logger

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:

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

Tip

Want a custom response format?

Define the formatError property in setConfig. More in Configuration

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 automatically maps non-AppError instances (e.g., database errors) to HTTP responses.

Supported mappings:

  • JWT: JsonWebTokenError, TokenExpiredError, NotBeforeError → mapped to 401 Unauthorized
  • express-validator: (v1.7.0+) FieldValidationError, GroupedAlternativeValidationError, AlternativeValidationError → mapped to 422 Unprocessable Content and UnknownFieldsError → mapped to 400 Bad Request
  • 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, 409 colflict).
  • Prisma: PrismaClientKnownRequestError, PrismaClientUnknownRequestError, PrismaClientRustPanicError, PrismaClientInitializationError, PrismaClientValidationError
  • Sequelize: SequelizeUniqueConstraintError, SequelizeValidationError, SequelizeForeignKeyConstraintError, SequelizeOptimisticLockError, SequelizeEmptyResultError, SequelizeDatabaseError, SequelizeConnectionError, SequelizeTimeoutError, SequelizeConnectionRefusedError, SequelizeHostNotFoundError, SequelizeHostNotReachableError, SequelizeAccessDeniedError
  • JS Native: ReferenceError, TypeError → mapped to 500. SyntaxError is handled (400 for bad JSON body, 500 for code errors).

Supported Prisma Error Codes:

Error Code Dev Message Prod Message HTTP Status
P2000 Value too long for column: ... Invalid input value 400
P2001 Record does not exist: ... Resource not found 404
P2002 Unique constraint failed: ... Resource already exists 409
P2003 Foreign key constraint failed: ... Invalid reference 400
P2005 The value stored in the database for the field is invalid for the field's type: ... Invalid data 400
P2006 The provided value for the field is not valid: ... Invalid input value 400
P2007 Data validation error: ... Invalid reference 400
P2011 Null constraint violation: ... Required value is missing 400
P2014 Required relation violation: ... Invalid relation 400
P2015 A related record could not be found: ... Requested resource not found 404
P2021 Table does not exist: ... Internal server error 500
P2022 Column does not exist: ... Internal server error 500
P2025 Record not found: ... Resource not found 404
P2027 Multiple errors occurred on the database during query execution: ... Internal server error 500
P1001 Cannot reach database: ... Service unavailable 503
P1002 Database timeout: ... Service unavailable 503
P1003 Database does not exist: ... Internal server error 500

Note

Example Prisma output for dev:

[2026-07-23T12:01:34.442Z] POST /prisma/p2003 
MESSAGE: Prisma P2003: [PrismaClientKnownRequestError] Foreign key constraint failed: { modelName: Post }; { field_name: Post_authorId_fkey (index) }  Operation: `prisma.post.create()` 
StatusCode: 400 
Stack: Error: Prisma P2003: [PrismaClientKnownRequestError] Foreign key constraint failed: { modelName: Post }; { field_name: Post_authorId_fkey (index) } 
Operation: `prisma.post.create()`
at BadRequest C:\...
Operational: true

Supported Sequelize errors:

Error Code / Type Prod Message HTTP Status
SequelizeValidationError email must be unique 400
SequelizeUniqueConstraintError Resource already exists 409
SequelizeForeignKeyConstraintError Invalid reference 409
SequelizeOptimisticLockError Resource conflict occurred 409
SequelizeEmptyResultError Resource not found 404
SequelizeDatabaseError Database error occurred 500
SequelizeConnectionError Database connection error occurred 503
SequelizeTimeoutError Database timeout error occurred 504
SequelizeConnectionRefusedError Database connection error occurred 503
SequelizeHostNotFoundError Database connection error occurred 503
SequelizeHostNotReachableError Database connection error occurred 503
SequelizeAccessDeniedError Database connection error occurred 503

Note

Example Sequelize output for dev:

[2026-07-23T13:02:44.783Z] POST /sequelize/unique-constraint 
MESSAGE: Sequelize: [UniqueConstraintError]: sku must be unique 
StatusCode: 409 
Stack: Error: Sequelize: [UniqueConstraintError]: sku must be unique
at Conflict C:...
Operational: true

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

About

DS Express Errors is library for standardizing error handling in Node.js applications built with Express.

Resources

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages