Configuration


1. Overview

The configuration system is accessed via setConfig(options).
All configuration must be provided before the error handler middleware is initialized.

Tip

You can explore the errors the library may throw when it is configured incorrectly.

Explore Library Errors References →

2. Default Configuration Interface

The setConfig function accepts a single object parameter with optional properties. The configuration is stored in a module-level singleton and affects all subsequent error handling operations.

Default configuration settings:

When setConfig is not called, the library uses the following default configuration defined in

javascript
setConfig({
    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
        } : {})
    })
})

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

Explore Library Errors References →

3. Configuration Options Reference

customMappers

Type: ErrorMapper[]

Default: []

Validation: Must be an array of synchronous functions (no async functions or Promises)

An ordered array of custom error mapping functions executed before built-in mappers. Each mapper receives an error and request object, returning either a transformed AppError or undefined/null to pass to the next mapper.

Interface:

typescript
interface ErrorMapper {
    (err: AppError | Error, req: Request): AppError | Error | undefined | null;
}

Execution Order:

  1. Custom mappers (array order)
  2. Library-specific mappers (filtered by needMappers)
  3. Name-based mapper (native errors)
  4. Fallback (500 Internal Server Error)

Example:

javascript
setConfig({
    customMappers: [
        (err) => {
            if (err.name === 'PaymentGatewayError') {
                return Errors.PaymentRequired('Payment processing failed');
            }
        },
        (err) => {
            if (err.code === 'ECONNREFUSED') {
                return Errors.ServiceUnavailable('External service unavailable');
            }
        }
    ]
});

errorClasses

Type: ErrorClasses | null

Default: null

Validation: Must be an object

Provides error constructor references for strict instanceof checks instead of duck-typing.

Limited Support

Currently (v1.8.1+) supports Zod and Joi validation libraries.

And from (1.9.x) supports Prisma and Sequelize.


Other libraries strict support is in progress.

Interface:

typescript
interface ErrorClasses {
    Zod?: { ZodError: new (...args: any[]) => Error }
    Joi?: { ValidationError: new (...args: any[]) => Error }
    Sequelize?: SequelizeErrorClasses;
    Prisma?: PrismaErrorClasses;
}
type ErrorConstructor = new (...args: any[]) => Error;

export interface SequelizeErrorClasses {
    ValidationError?: ErrorConstructor;
    ForeignKeyConstraintError?: ErrorConstructor;
    UniqueConstraintError?: ErrorConstructor;
    OptimisticLockError?: ErrorConstructor;
    EmptyResultError?: ErrorConstructor;

    DatabaseError?: ErrorConstructor;
    TimeoutError?: ErrorConstructor;

    ConnectionError?: ErrorConstructor;
    ConnectionRefusedError?: ErrorConstructor;
    HostNotFoundError?: ErrorConstructor;
    HostNotReachableError?: ErrorConstructor;
    AccessDeniedError?: ErrorConstructor;
    ConnectionAcquireTimeoutError?: ErrorConstructor;
    ConnectionTimedOutError?: ErrorConstructor;
}

export interface PrismaErrorClasses {
    PrismaClientKnownRequestError?: ErrorConstructor;
    PrismaClientUnknownRequestError?: ErrorConstructor;
    PrismaClientRustPanicError?: ErrorConstructor;
    PrismaClientInitializationError?: ErrorConstructor;
    PrismaClientValidationError?: ErrorConstructor;
}

Without errorClasses, the library uses duck-typing to detect error types (checking for properties like error.issues for Zod).
With errorClasses, the library performs strict instanceof checks for more reliable type detection.

Example:

javascript
import { zod } from 'zod'
import Joi from 'joi'
import { Sequelize } from 'sequelize'
import { Prisma } from '@prisma/client'

setConfig({
    errorClasses: {
        Zod: z,
        Joi: Joi,
        Prisma,
        Sequelize
    }
});

needMappers

Type: [union] = 'zod', 'prisma', 'joi', 'sequelize', 'mongoose', 'expressValidator'

Default: null (all mappers enabled)

Validation: Must be an array

Enables selective activation of library-specific error mappers, reducing overhead when certain libraries are not used in the application.

Valid Mapper Names:

  • 'zod' - Zod validation errors
  • 'joi' - Joi validation errors
  • 'expressValidator' - express-validator errors
  • 'mongoose' - Mongoose/MongoDB errors
  • 'prisma' - Prisma ORM errors
  • 'sequelize' - Sequelize ORM errors

Warning

When needMappers is null, all mappers are active. When specified, only listed mappers execute. Custom mappers and name-based mappers always execute regardless of this setting.

Example:

javascript
setConfig({
    needMappers: ['zod', 'prisma'] // Only map Zod and Prisma errors
});

customLogger

Type: Logger | null

Default: null

Validation: Must be an object with methods error, warn, info, debug

Replaces the built-in console logger with a custom logging implementation. When provided, all logging calls are delegated to this logger instead of the internal logger.

Interface:

typescript
export interface Logger {
    error(message: any, ...args: any[]): void
    warn(message: any, ...args: any[]): void
    info(message: any, ...args: any[]): void
    debug(message: any, ...args: any[]): void
} 

Example:

javascript
import winston from 'winston'

const levels = {
  error: 0,
  warn: 1,
  info: 2,
  debug: 3,
};

const logger = winston.createLogger({
    level: levels,
    format: winston.format.json(),
    transports: [new winston.transports.File({ filename: 'error.log' })]
});

setConfig({ customLogger: logger });

maxLoggerRequests

Type: number

Default: 100

Validation: Must be a number

Sets the maximum number of log entries allowed per minute. This rate limiting prevents log flooding in high-error scenarios, protecting log storage and performance.

When the limit is exceeded, additional log requests are silently dropped until the next minute window.

Example:

javascript
setConfig({
    maxLoggerRequests: 1000 // Allow 1000 logs per minute
});

devEnvironments

Type: string[]

Default: ['dev', 'development']

Validation: Must be a array

Defines which NODE_ENV values are considered development environments. This controls error verbosity and stack trace inclusion in responses.

Checks if process.env.NODE_ENV matches any value in this array. When true:

  • Full stack traces included in responses
  • Request method and URL exposed
  • Detailed validation error messages
  • Database field names revealed

Example:

javascript
setConfig({
    devEnvironments: ['development', 'dev', 'staging', 'local']
});

formatError

Type: (err: AppError | Error, options: ConfigOptions) => any

Default: See below

Validation: Must be a function

Custom formatter for the final error response sent to clients. This enables adherence to API standards like JSON:API or legacy response formats.

Interface:

typescript
interface ConfigOptions {
    req: Request
    isDev: boolean
}

type FormatError = (err: AppError | Error, options: ConfigOptions) => any

Default Implementation:

javascript
formatError: (err, {req, isDev}) => ({ 
    status: err.isOperational ? 'fail' : 'error',
    message: err.message,
    ...(isDev ? { 
        method: req.method,
        url: req.originalUrl,
        stack: err.stack
    } : {})
})

The formatter receives the error object (which is always an AppError instance after mapping) and a context object containing the Express request and environment flag.

Example (JSON:API format):

javascript
setConfig({
    formatError: (err, {req, isDev}) => ({
        errors: [{
            status: err.statusCode.toString(),
            title: err.message,
            detail: isDev ? err.stack : undefined,
            source: isDev ? {
                pointer: req.originalUrl
            } : undefined
        }]
    })
});

4. Configuration Timing

Configuration must be set before the error handler middleware is registered:

javascript
import express from 'express'
import { setConfig, errorHandler } from 'ds-express-errors'

const app = express();

// 1. Set configuration FIRST
setConfig({
    customLogger: winston.createLogger({...}),
    devEnvironments: ['development', 'staging'],
    maxLoggerRequests: 500
});

// 2. Register routes
app.get('/api/users', ...);

// 3. Register error handler LAST
app.use(errorHandler);

app.listen(3000);

Once errorHandler is invoked, it reads from the configuration singleton. Calling setConfig after error handler registration affects subsequent errors but not errors already in flight.