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

@khni/error-handler

v2.1.1

Published

@khni/error-handler is a modular, extensible error handling system for Node.js and TypeScript with custom errors, HTTP errors, serializers, handlers, and mappers.

Readme

@khni/error-handler

A robust, TypeScript-first error handling library providing structured error classes with built-in logging support, HTTP error handling, and standardized error responses.

Features

  • 🏗 Type-Safe Errors - Fully typed error classes with generic code support
  • 🌐 HTTP Error Support - Specialized errors for HTTP APIs with status codes
  • 📝 Structured Logging - Built-in log levels and metadata support
  • 🔍 Error Chaining - Support for error causes with native cause property
  • 🎯 Validation Errors - Standardized input validation error format
  • 🔄 Error Mapping - Convert business errors to HTTP errors automatically
  • 📚 API Documentation - Comprehensive documentation generated with TypeScript
  • 🛡 Production Ready - Express middleware and error handling strategies

Installation

npm install @khni/error-handler
pnpm add @khni/error-handler
yarn add @khni/error-handler

Quick Start

Basic CustomError Usage

import { CustomError } from "@khni/error-handler";

class BusinessError extends CustomError<
  "INVALID_OPERATION" | "MISSING_REQUIRED"
> {
  constructor(message: string, code: "INVALID_OPERATION" | "MISSING_REQUIRED") {
    super({
      name: "BusinessError",
      message,
      code,
      logLevel: "warn",
    });
  }
}

// Usage
throw new BusinessError("Operation not allowed", "INVALID_OPERATION");

HTTP Error Usage

import { HttpError } from "@khni/error-handler";

class NotFoundError extends HttpError {
  statusCode = 404;

  constructor(resource: string) {
    super({
      name: "NotFoundError",
      message: `Resource not found: ${resource}`,
      responseMessage: `${resource} not found`,
      code: "RESOURCE_NOT_FOUND",
      logLevel: "info",
    });
  }
}

// Usage in Express.js
app.get("/users/:id", (req, res) => {
  const user = findUser(req.params.id);
  if (!user) {
    throw new NotFoundError("User");
  }
  res.json(user);
});

Complete Express.js Integration

1. Setup Error Handling Middleware

import express from "express";
import { createErrHandlerMiddleware } from "@khni/error-handler";

const app = express();

// Your routes and other middleware
app.use(express.json());
app.use("/api", apiRoutes);

// Error handling middleware (must be last)
app.use(createErrHandlerMiddleware());

app.listen(3000);

2. With Custom Logger

import { createErrHandlerMiddleware, ILogger } from "@khni/error-handler";

class ConsoleLogger implements ILogger {
  error(message: string, meta?: {}) {
    console.error(`[ERROR] ${message}`, meta);
  }
  warn(message: string, meta?: {}) {
    console.warn(`[WARN] ${message}`, meta);
  }
  info(message: string, meta?: {}) {
    console.info(`[INFO] ${message}`, meta);
  }
  debug(message: string, meta?: {}) {
    console.debug(`[DEBUG] ${message}`, meta);
  }
}

const logger = new ConsoleLogger();
app.use(createErrHandlerMiddleware(logger));

Error Types

CustomError

Base class for all custom errors with code type safety:

import { CustomError } from "@khni/error-handler";

class DatabaseError extends CustomError<"CONNECTION" | "TIMEOUT" | "QUERY"> {
  constructor(code: "CONNECTION" | "TIMEOUT" | "QUERY", cause?: Error) {
    super({
      name: "DatabaseError",
      message: `Database error: ${code}`,
      code,
      logLevel: "error",
      cause,
    });
  }
}

// Usage
throw new DatabaseError("CONNECTION", connectionError);

HttpError

HTTP-specific errors with status codes and client-safe messages:

import { HttpError } from "@khni/error-handler";

class UnauthorizedError extends HttpError {
  statusCode = 401;

  constructor() {
    super({
      name: "UnauthorizedError",
      message: "Authentication required",
      responseMessage: "Please log in to access this resource",
      code: "UNAUTHORIZED",
      logLevel: "warn",
    });
  }
}

class BadRequestError extends HttpError {
  statusCode = 400;

  constructor(message: string, code: string = "BAD_REQUEST") {
    super({
      name: "BadRequestError",
      message,
      responseMessage: "Invalid request parameters",
      code,
      logLevel: "warn",
    });
  }
}

Input Validation with Zod Integration

1. Define Validation Schema

import { z } from "zod";
import { InputValidationError, zodErrorSerializer } from "@khni/error-handler";

const userRegistrationSchema = z.object({
  email: z.string().email("Invalid email format"),
  password: z
    .string()
    .min(8, "Password must be at least 8 characters")
    .regex(/[A-Z]/, "Password must contain at least one uppercase letter")
    .regex(/[0-9]/, "Password must contain at least one number"),
  profile: z.object({
    firstName: z.string().min(1, "First name is required"),
    age: z.number().min(18, "Must be at least 18 years old"),
  }),
});

2. Create Validation Function

export const validateUserRegistration = async (data: unknown) => {
  try {
    return await userRegistrationSchema.parseAsync(data);
  } catch (error) {
    if (error instanceof ZodError) {
      throw new InputValidationError(error, zodErrorSerializer);
    }
    throw error;
  }
};

3. Use in Express Controller

app.post("/register", async (req, res, next) => {
  try {
    const validatedData = await validateUserRegistration(req.body);
    const user = await userService.create(validatedData);
    res.status(201).json(user);
  } catch (error) {
    next(error); // Will be handled by our error middleware
  }
});

Error Response Formats

HTTP Error Response

{
  "errorType": "Server",
  "error": {
    "name": "NotFoundError",
    "message": "Resource not found",
    "code": "RESOURCE_NOT_FOUND"
  }
}

Input Validation Error Response

{
  "errorType": "InputValidation",
  "error": {
    "name": "ValidationError",
    "errors": [
      {
        "field": "email",
        "messages": ["Invalid email format"]
      },
      {
        "field": "password",
        "messages": [
          "Password must be at least 8 characters",
          "Password must contain at least one uppercase letter"
        ]
      }
    ]
  }
}

Fallback Error Response

{
  "errorType": "Server",
  "error": {
    "name": "InternalServerError",
    "message": "An unexpected error occurred",
    "code": "INTERNAL_ERROR"
  }
}

Advanced Error Mapping

Business Logic to HTTP Error Mapping

import { errorMapper, CustomError } from "@khni/error-handler";

// 1. Define business error codes
const ErrorCodes = {
  USER_NOT_FOUND: "USER_NOT_FOUND",
  INVALID_EMAIL: "INVALID_EMAIL",
  INSUFFICIENT_PERMISSIONS: "INSUFFICIENT_PERMISSIONS",
} as const;

type ErrorCode = keyof typeof ErrorCodes;

// 2. Create business error class
class UserError extends CustomError<ErrorCode> {
  constructor(params: Omit<CustomErrorConstructor<ErrorCode>, "name">) {
    super({ ...params, name: "UserError" });
  }
}

// 3. Define HTTP mapping
const errorMapping: Record<
  ErrorCode,
  { statusCode: number; responseMessage: string }
> = {
  USER_NOT_FOUND: {
    statusCode: 404,
    responseMessage: "User not found",
  },
  INVALID_EMAIL: {
    statusCode: 400,
    responseMessage: "Please provide a valid email address",
  },
  INSUFFICIENT_PERMISSIONS: {
    statusCode: 403,
    responseMessage: "You do not have permission to perform this action",
  },
};

// 4. Use in service layer
class UserService {
  async getUser(userId: string) {
    const user = await userRepository.findById(userId);
    if (!user) {
      throw new UserError({
        message: `User with ID ${userId} not found`,
        code: "USER_NOT_FOUND",
        logLevel: "warn",
        meta: { userId },
      });
    }
    return user;
  }
}

// 5. Map in controller layer
app.get("/users/:id", async (req, res, next) => {
  try {
    const user = await userService.getUser(req.params.id);
    res.json(user);
  } catch (error) {
    if (error instanceof UserError) {
      // Convert business error to HTTP error
      next(errorMapper(error, errorMapping));
    } else {
      next(error);
    }
  }
});

Advanced Validation Service

For complex applications, create a dedicated validation service:

import { ValidationService } from "@khni/error-handler";

// Advanced validation with context
const user = await ValidationService.validate(userSchema, req.body, {
  entity: "User",
  action: "creation",
});

// Batch validation
const results = await ValidationService.validateAll([
  {
    schema: userSchema,
    data: req.body.user,
    context: { entity: "User", action: "create" },
  },
  {
    schema: profileSchema,
    data: req.body.profile,
    context: { entity: "Profile", action: "create" },
  },
]);

Custom Error Strategies

Creating Custom Error Handling Strategies

import { IErrorHandlingStrategy, Response } from "express";
import { MyCustomError } from "./MyCustomError.js";

export class CustomErrorStrategy implements IErrorHandlingStrategy {
  canHandle(err: Error): boolean {
    return err instanceof MyCustomError;
  }

  handle(err: Error, res: Response): void {
    const error = err as MyCustomError;

    // Custom handling logic
    res.status(error.customStatusCode).json({
      errorType: "Custom",
      error: error.toClientFormat(),
    });
  }
}

// Use in your error handler
const errorHandler = new ErrorHandler([
  new HttpErrorHandlerStrategy(/* ... */),
  new InputValidationErrorHandlerStrategy(/* ... */),
  new CustomErrorStrategy(), // Your custom strategy
  new FallbackErrorStrategy(/* ... */),
]);

Logging Integration

Structured Error Logging

import { ILogger } from "@khni/error-handler";

class StructuredLogger implements ILogger {
  error(message: string, meta?: {}) {
    // Integrate with your logging system (Winston, Pino, etc.)
    console.error(
      JSON.stringify({
        level: "error",
        message,
        timestamp: new Date().toISOString(),
        ...meta,
      })
    );
  }

  warn(message: string, meta?: {}) {
    console.warn(
      JSON.stringify({
        level: "warn",
        message,
        timestamp: new Date().toISOString(),
        ...meta,
      })
    );
  }

  info(message: string, meta?: {}) {
    console.info(
      JSON.stringify({
        level: "info",
        message,
        timestamp: new Date().toISOString(),
        ...meta,
      })
    );
  }

  debug(message: string, meta?: {}) {
    console.debug(
      JSON.stringify({
        level: "debug",
        message,
        timestamp: new Date().toISOString(),
        ...meta,
      })
    );
  }
}

// Use with error handler
const logger = new StructuredLogger();
app.use(createErrHandlerMiddleware(logger));

Testing

Testing Error Scenarios

import { describe, it, expect } from "vitest";
import { InputValidationError, zodErrorSerializer } from "@khni/error-handler";
import { ZodError } from "zod";

describe("Error Handling", () => {
  it("should create InputValidationError from ZodError", () => {
    const zodError = {
      issues: [
        { path: ["email"], message: "Invalid email", code: "invalid_string" },
      ],
    } as ZodError;

    const error = new InputValidationError(zodError, zodErrorSerializer);

    expect(error).toBeInstanceOf(InputValidationError);
    expect(error.name).toBe("ValidationError");
    expect(error.getFieldErrors("email")).toEqual(["Invalid email"]);
  });

  it("should handle error mapping", () => {
    const businessError = new UserError({
      message: "User not found",
      code: "USER_NOT_FOUND",
      logLevel: "warn",
    });

    const httpError = errorMapper(businessError, errorMapping);

    expect(httpError.statusCode).toBe(404);
    expect(httpError.responseMessage).toBe("User not found");
  });
});

API Documentation

For complete API documentation, generate and view the docs:

# Generate documentation
npm run build
npm run api:extract
npm run api:docs

# View generated documentation
open docs/README.md

Key exports:

Best Practices

1. Error Hierarchy

// Domain-specific errors extend CustomError
class DomainError extends CustomError<DomainErrorCode> {
  // Domain-specific logic
}

// HTTP errors extend HttpError
class ApiError extends HttpError {
  // API-specific logic
}

2. Consistent Error Codes

// Use const assertions for type safety
const ErrorCodes = {
  NOT_FOUND: "NOT_FOUND",
  VALIDATION_FAILED: "VALIDATION_FAILED",
  UNAUTHORIZED: "UNAUTHORIZED",
} as const;

type ErrorCode = keyof typeof ErrorCodes;

3. Proper Middleware Order

// ✅ Correct order
app.use(express.json());
app.use(routes);
app.use(createErrHandlerMiddleware()); // Always last

// ❌ Incorrect order
app.use(createErrHandlerMiddleware());
app.use(routes); // Errors in routes won't be caught

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Update documentation
  6. Submit a pull request

License

MIT License - see LICENSE file for details.


This library provides a complete, production-ready error handling solution for TypeScript applications with Express.js integration, type safety, and comprehensive error management capabilities.