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

@verisure-italy/express-error-handler-middleware

v1.7.2

Published

Express error handler middleware with support for domain-specific errors

Readme

Error Handler Middleware

A comprehensive Express error handler middleware that provides standardized error responses for all domain-specific errors from various packages including router-middleware and authentication-middleware.

Features

  • Standardized Error Responses - Consistent error format across all error types
  • Domain Error Support - Built-in handling for validation, authentication, authorization, and not found errors
  • Zod Validation Errors - Detailed field-by-field validation error messages
  • Type-Safe - Full TypeScript support with detailed error types
  • Customizable - Custom formatters, error handlers, and skip conditions
  • Environment Aware - Different behavior for development and production
  • Logging Support - Built-in error logging with custom handler support
  • Stack Traces - Optional stack traces in development mode

Installation

pnpm add @verisure-italy/error-handler-middleware

Quick Start

Basic Usage

import express from 'express'
import { errorHandler } from '@verisure-italy/error-handler-middleware'

const app = express()

// ... your routes ...

// Add error handler as the last middleware
app.use(errorHandler())

With Configuration

import { errorHandler } from '@verisure-italy/error-handler-middleware'

app.use(
  errorHandler({
    logErrors: true,
    showDetails: process.env.NODE_ENV !== 'production',
    includeStackTrace: process.env.NODE_ENV === 'development',
  })
)

Error Response Format

All errors follow a consistent structure:

interface ErrorResponse {
  error: string           // Error type (e.g., "Validation Error")
  message: string         // Human-readable message
  statusCode: number      // HTTP status code
  code: string           // Machine-readable error code
  timestamp: string      // ISO 8601 timestamp
  path?: string          // Request path where error occurred
  details?: any          // Additional error details (environment-dependent)
}

Supported Error Types

1. Validation Errors (422)

From router-middleware with Zod:

// Request
POST /api/users
{
  "username": "",
  "roles": []
}

// Response
{
  "error": "Validation Error",
  "message": "The request contains invalid data",
  "statusCode": 422,
  "code": "VALIDATION_ERROR",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users",
  "details": [
    {
      "field": "username",
      "message": "String must contain at least 1 character(s)",
      "code": "too_small",
      "value": ""
    },
    {
      "field": "roles",
      "message": "Array must contain at least 1 element(s)",
      "code": "too_small",
      "value": []
    }
  ]
}

2. Authentication Errors (401)

From authentication-middleware:

// Response
{
  "error": "Authentication Error",
  "message": "Invalid token",
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users"
}

3. Authorization Errors (403)

From router-middleware ACL:

// Response
{
  "error": "Authorization Error",
  "message": "You do not have permission to access this resource",
  "statusCode": 403,
  "code": "FORBIDDEN",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users/123"
}

4. Not Found Errors (404)

From router-middleware param resolver:

// Response
{
  "error": "Not Found",
  "message": "User not found",
  "statusCode": 404,
  "code": "NOT_FOUND",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users/unknown-id"
}

5. Generic Errors (500)

Any unhandled error:

// Development Response (showDetails: true)
{
  "error": "Internal Server Error",
  "message": "Cannot read property 'id' of undefined",
  "statusCode": 500,
  "code": "INTERNAL_ERROR",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users",
  "details": {
    "name": "TypeError",
    "stack": [
      "TypeError: Cannot read property 'id' of undefined",
      "    at UserController.create (/app/controllers/user.js:15:20)",
      "    ..."
    ]
  }
}

// Production Response (showDetails: false)
{
  "error": "Internal Server Error",
  "message": "An unexpected error occurred",
  "statusCode": 500,
  "code": "INTERNAL_ERROR",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users"
}

Configuration

ErrorHandlerConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | includeStackTrace | boolean | NODE_ENV !== 'production' | Include stack trace in error response | | logErrors | boolean | NODE_ENV === 'development' | Log errors to console | | showDetails | boolean | NODE_ENV !== 'production' | Show detailed error information | | onError | (error, req, res) => void \| Promise<void> | undefined | Custom error handler (for logging, monitoring, etc.) | | skipError | (error, req) => boolean | () => false | Skip handling for specific errors | | formatters | object | {} | Custom formatters for specific error types |

Advanced Usage

Custom Error Logging

import { errorHandler } from '@verisure-italy/error-handler-middleware'
import { logger } from './logger'

app.use(
  errorHandler({
    onError: async (error, req, res) => {
      // Log to external service (e.g., Sentry, DataDog)
      await logger.error('API Error', {
        error: error.message,
        stack: error.stack,
        path: req.path,
        method: req.method,
        userId: req._auth?.user?.id,
        statusCode: res.statusCode,
      })
    },
  })
)

Skip Specific Errors

app.use(
  errorHandler({
    skipError: (error, req) => {
      // Skip 404 errors on static files
      if (error.statusCode === 404 && req.path.startsWith('/static')) {
        return true
      }
      
      // Skip specific error types
      if (error.code === 'CUSTOM_ERROR') {
        return true
      }
      
      return false
    },
  })
)

Custom Error Formatters

app.use(
  errorHandler({
    formatters: {
      validation: (error) => ({
        error: 'Invalid Input',
        message: 'Please check your input and try again',
        statusCode: 422,
        code: 'INVALID_INPUT',
        timestamp: new Date().toISOString(),
        details: {
          fields: error.errors?.map(e => e.field) || [],
        },
      }),
      authentication: (error) => ({
        error: 'Auth Failed',
        message: 'Please log in to continue',
        statusCode: 401,
        code: 'AUTH_REQUIRED',
        timestamp: new Date().toISOString(),
      }),
    },
  })
)

Different Configs for Different Environments

const errorConfig = {
  development: {
    logErrors: true,
    showDetails: true,
    includeStackTrace: true,
  },
  production: {
    logErrors: false,
    showDetails: false,
    includeStackTrace: false,
    onError: async (error, req) => {
      // Send to monitoring service
      await sentry.captureException(error, {
        extra: { path: req.path, method: req.method },
      })
    },
  },
}

app.use(
  errorHandler(errorConfig[process.env.NODE_ENV] || errorConfig.development)
)

Integration with Router Middleware

Perfect integration with @verisure-italy/router-middleware:

import express from 'express'
import { createRouter, createRestResource } from '@verisure-italy/router-middleware'
import { errorHandler } from '@verisure-italy/error-handler-middleware'
import { User, userSchema } from '@verisure-italy/aaa-types'

const app = express()

// Router middleware
const userRoutes = createRestResource<User>({
  dataModel: 'user',
  basePath: '/users',
  sharedOptions: {
    secure: true,
    acl: { allow: { roles: ['ROLE_AAA_ADMIN'] } },
  },
  methodOptions: {
    create: {
      validate: { body: userSchema.omit({ id: true }) },
    },
  },
})

app.use('/api', createRouter(userRoutes, { /* config */ }))

// Error handler (MUST be last)
app.use(errorHandler({
  logErrors: true,
  showDetails: process.env.NODE_ENV !== 'production',
}))

Error handling flow:

  1. Validation Error (from Zod in router-middleware)

    • Middleware catches validation error
    • Error handler formats with field-by-field details
    • Returns 422 with detailed validation errors
  2. Authentication Error (from authentication-middleware)

    • Middleware catches auth error
    • Error handler formats
    • Returns 401 with clear auth message
  3. Authorization Error (from ACL in router-middleware)

    • Middleware catches forbidden error
    • Error handler formats
    • Returns 403 with permission message

Type Checking

The middleware provides TypeScript utilities to check error types:

import {
  isDomainError,
  isValidationError,
  isAuthenticationError,
  isAuthorizationError,
  isNotFoundError,
  isZodError,
} from '@verisure-italy/error-handler-middleware'

app.use(
  errorHandler({
    onError: (error) => {
      if (isValidationError(error)) {
        console.log('Validation failed:', error.errors)
      } else if (isAuthenticationError(error)) {
        console.log('Auth failed')
      } else if (isZodError(error)) {
        console.log('Zod validation issues:', error.issues)
      }
    },
  })
)

Best Practices

1. Always Place Last

The error handler MUST be the last middleware:

// ✅ Correct order
app.use(bodyParser.json())
app.use(authMiddleware)
app.use('/api', router)
app.use(errorHandler())  // Last!

// ❌ Wrong order
app.use(errorHandler())  // Too early!
app.use('/api', router)

2. Different Configs for Environments

// ✅ Good
app.use(errorHandler({
  showDetails: process.env.NODE_ENV !== 'production',
  logErrors: process.env.NODE_ENV === 'development',
}))

// ❌ Bad - exposes sensitive info in production
app.use(errorHandler({
  showDetails: true,
  includeStackTrace: true,
}))

3. Use Custom Error Handler for Monitoring

// ✅ Good - log to monitoring service
app.use(errorHandler({
  onError: async (error, req) => {
    if (process.env.NODE_ENV === 'production') {
      await monitoring.captureException(error, {
        tags: { path: req.path, method: req.method },
      })
    }
  },
}))

4. Graceful Degradation

// ✅ Good - fallback if error handler fails
app.use(errorHandler())

// Fallback handler
app.use((error, req, res, next) => {
  res.status(500).json({ error: 'Something went wrong' })
})

Examples

Complete Express App

import express from 'express'
import { createRouter, createRestResource } from '@verisure-italy/router-middleware'
import { dynamoAuthMiddleware } from '@verisure-italy/authentication-middleware'
import { errorHandler } from '@verisure-italy/error-handler-middleware'
import { User, userSchema } from '@verisure-italy/aaa-types'

const app = express()

// Body parser
app.use(express.json())

// Authentication
app.use(
  dynamoAuthMiddleware({
    dynamoConfig: { region: 'eu-west-1' },
    tokenTableName: 'access_tokens',
    userTableName: 'users',
  })
)

// API routes
const userRoutes = createRestResource<User>({
  dataModel: 'user',
  basePath: '/users',
  methods: ['create', 'read', 'update', 'list', 'delete'],
  sharedOptions: {
    secure: true,
    acl: { allow: { roles: ['ROLE_AAA_ADMIN'] } },
  },
  methodOptions: {
    create: {
      validate: { body: userSchema.omit({ id: true }) },
    },
    update: {
      validate: { body: userSchema.partial().omit({ id: true }) },
    },
  },
})

app.use(
  '/api',
  createRouter(userRoutes, {
    dataModels: { user: { tableName: 'users', idField: 'id' } },
  })
)

// 404 handler
app.use((req, res, next) => {
  const error: any = new Error('Route not found')
  error.statusCode = 404
  error.code = 'NOT_FOUND'
  next(error)
})

// Error handler (MUST be last!)
app.use(
  errorHandler({
    logErrors: true,
    showDetails: process.env.NODE_ENV !== 'production',
    onError: async (error, req) => {
      // Log to monitoring service in production
      if (process.env.NODE_ENV === 'production') {
        await logger.error(error, {
          path: req.path,
          method: req.method,
        })
      }
    },
  })
)

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000')
})

Error Response Examples

Validation Error with Multiple Fields

{
  "error": "Validation Error",
  "message": "The request contains invalid data",
  "statusCode": 422,
  "code": "VALIDATION_ERROR",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users",
  "details": [
    {
      "field": "username",
      "message": "String must contain at least 1 character(s)",
      "code": "too_small",
      "value": ""
    },
    {
      "field": "roles",
      "message": "Array must contain at least 1 element(s)",
      "code": "too_small",
      "value": []
    },
    {
      "field": "email",
      "message": "Invalid email",
      "code": "invalid_string",
      "value": "not-an-email"
    }
  ]
}

Authentication Error

{
  "error": "Authentication Error",
  "message": "Token expired",
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users"
}

Authorization Error

{
  "error": "Authorization Error",
  "message": "You do not have permission to access this resource",
  "statusCode": 403,
  "code": "FORBIDDEN",
  "timestamp": "2025-10-24T10:30:00.000Z",
  "path": "/api/users/123"
}

License

MIT