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

@munisn/app-errors

v1.1.2

Published

Framework-agnostic error handling library for Node.js applications. Provides consistent, serializable error models for Express, NestJS, and microservices.

Readme

@munisn/app-errors

A framework-agnostic error handling library for Node.js applications. Provides consistent, serializable error models for Express, NestJS, and microservices.

npm version TypeScript

🎯 Philosophy

This library provides a contract for errors across your microservices architecture. It models errors in a consistent, typed, and serializable way without coupling to any specific infrastructure (HTTP frameworks, event systems, logging libraries, etc.).

What This Library Does ✅

  • Models application errors with consistent structure
  • Provides serializable error representations (JSON for HTTP, Events for NATS/Kafka)
  • Offers type-safe error classes for common scenarios
  • Supports error chaining (ES2022 cause)
  • Preserves stack traces correctly

What This Library Does NOT Do ❌

  • Does NOT handle HTTP requests/responses
  • Does NOT publish events to NATS/Kafka
  • Does NOT perform logging
  • Does NOT include middleware or framework-specific code
  • Does NOT depend on Express, NestJS, NATS, or any infrastructure

Infrastructure lives outside this library. The library only models errors; you handle the rest.

📦 Installation

npm install @munisn/app-errors

🚀 Quick Start

Basic Usage

Using sendTelegramAlert Parameter

All error classes accept an optional sendTelegramAlert parameter to control Telegram notifications:

TypeScript Example:

import { ValidationError, ForbiddenError } from "@munisn/app-errors";

// ValidationError - Basic usage (no telegram alert)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// ValidationError - With telegram alert for suspicious patterns
if (containsSQLInjection(email)) {
  throw new ValidationError(
    "Invalid email format detected",
    { field: "email", value: email, suspicious: true },
    undefined, // cause
    true // sendTelegramAlert - potential security threat
  );
}

// ForbiddenError - With telegram alert for security violations
if (!user.hasPermission("DELETE_USER")) {
  throw new ForbiddenError(
    "Insufficient permissions to delete user",
    { userId: user.id, requiredPermission: "DELETE_USER" },
    undefined, // cause
    true // sendTelegramAlert - security violation
  );
}

JavaScript Example:

const { ValidationError, ForbiddenError } = require("@munisn/app-errors");

// ValidationError - Basic usage (no telegram alert)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// ValidationError - With telegram alert for suspicious patterns
if (containsSQLInjection(email)) {
  throw new ValidationError(
    "Invalid email format detected",
    { field: "email", value: email, suspicious: true },
    undefined, // cause
    true // sendTelegramAlert - potential security threat
  );
}

// ForbiddenError - With telegram alert for security violations
if (!user.hasPermission("DELETE_USER")) {
  throw new ForbiddenError(
    "Insufficient permissions to delete user",
    { userId: user.id, requiredPermission: "DELETE_USER" },
    undefined, // cause
    true // sendTelegramAlert - security violation
  );
}

TypeScript Example

import {
  ValidationError,
  NotFoundError,
  ForbiddenError,
} from "@munisn/app-errors";

// Throw a validation error (no telegram alert by default)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// Throw a validation error with telegram alert
if (!isValidEmailFormat(email)) {
  throw new ValidationError(
    "Invalid email format",
    { field: "email", value: email },
    undefined, // cause
    true // sendTelegramAlert - alert for suspicious input patterns
  );
}

// Throw a forbidden error with telegram alert (security-related)
if (!user.hasPermission("DELETE_USER")) {
  throw new ForbiddenError(
    "Insufficient permissions to delete user",
    { userId: user.id, requiredPermission: "DELETE_USER" },
    undefined, // cause
    true // sendTelegramAlert - security violations should be alerted
  );
}

// Use in async operations
try {
  const user = await findUser(id);
  if (!user) {
    throw new NotFoundError("User not found", { userId: id });
  }
} catch (error) {
  // Handle or re-throw
}

JavaScript Example

const {
  ValidationError,
  NotFoundError,
  ForbiddenError,
} = require("@munisn/app-errors");

// Throw a validation error (no telegram alert by default)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// Throw a validation error with telegram alert
if (!isValidEmailFormat(email)) {
  throw new ValidationError(
    "Invalid email format",
    { field: "email", value: email },
    undefined, // cause
    true // sendTelegramAlert
  );
}

// Throw a forbidden error with telegram alert (security-related)
if (!user.hasPermission("DELETE_USER")) {
  throw new ForbiddenError(
    "Insufficient permissions to delete user",
    { userId: user.id, requiredPermission: "DELETE_USER" },
    undefined, // cause
    true // sendTelegramAlert - security violations should be alerted
  );
}

// Use in async operations
try {
  const user = await findUser(id);
  if (!user) {
    throw new NotFoundError("User not found", { userId: id });
  }
} catch (error) {
  // Handle or re-throw
}

HTTP Response (Express)

import { AppError } from "@munisn/app-errors";
import express from "express";

app.use(
  (
    err: Error,
    req: express.Request,
    res: express.Response,
    next: express.NextFunction
  ) => {
    if (err instanceof AppError) {
      return res.status(err.statusCode || 500).json(err.toJSON());
    }
    // Handle unknown errors...
  }
);

Event Publishing (NATS)

import { AppError, ErrorEventContext, Enviroment } from "@munisn/app-errors";
import nats from "nats";
import { Request } from "express";

async function publishError(error: AppError, req: Request) {
  // Build context from HTTP request
  const context: ErrorEventContext = {
    api_name: process.env.API_NAME || "users-api",
    service_name: process.env.SERVICE_NAME || "users-service",
    endpoint: req.originalUrl,
    http_method: req.method,
    user_id: req.user?.id || null,
    client_ip: req.ip,
    request_payload: req.body,
    enviroment: Enviroment.PRODUCTION,
  };

  // Generate error event with context
  const event = error.toEvent(context);

  await nats.publish("errors.create", JSON.stringify(event));
}

💡 sendTelegramAlert Usage Examples

When to Use sendTelegramAlert

Use sendTelegramAlert: true for:

  • Security violations: Unauthorized access attempts, permission violations
  • Suspicious patterns: SQL injection attempts, XSS attacks, suspicious input
  • Critical errors: Database failures, external service outages
  • High-severity issues: Errors that require immediate attention

Use sendTelegramAlert: false (default) for:

  • Normal validations: Expected user input errors
  • Business logic: Normal domain rule violations
  • Not found errors: Expected 404 scenarios

Complete Example: ValidationError

TypeScript:

import { ValidationError } from "@munisn/app-errors";

// Normal validation (no alert)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// Suspicious pattern detection (with alert)
function containsSQLInjection(input: string): boolean {
  const sqlPatterns =
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER)\b)|('|(\\')|(;)|(--)|(\*)|(\%27)|(\%00))/i;
  return sqlPatterns.test(input);
}

if (containsSQLInjection(email)) {
  throw new ValidationError(
    "Invalid input detected - possible SQL injection attempt",
    {
      field: "email",
      value: email.substring(0, 50), // Truncate for security
      suspicious: true,
      pattern: "SQL_INJECTION",
    },
    undefined, // cause
    true // sendTelegramAlert - security threat
  );
}

JavaScript:

const { ValidationError } = require("@munisn/app-errors");

// Normal validation (no alert)
if (!email) {
  throw new ValidationError("Email is required", { field: "email" });
}

// Suspicious pattern detection (with alert)
function containsSQLInjection(input) {
  const sqlPatterns =
    /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER)\b)|('|(\\')|(;)|(--)|(\*)|(\%27)|(\%00))/i;
  return sqlPatterns.test(input);
}

if (containsSQLInjection(email)) {
  throw new ValidationError(
    "Invalid input detected - possible SQL injection attempt",
    {
      field: "email",
      value: email.substring(0, 50), // Truncate for security
      suspicious: true,
      pattern: "SQL_INJECTION",
    },
    undefined, // cause
    true // sendTelegramAlert - security threat
  );
}

Complete Example: ForbiddenError

TypeScript:

import { ForbiddenError } from "@munisn/app-errors";

// Check permissions before sensitive operations
async function deleteUser(userId: string, requester: User) {
  // Security check with telegram alert
  if (!requester.hasPermission("DELETE_USER")) {
    throw new ForbiddenError(
      "Insufficient permissions to delete user",
      {
        userId: requester.id,
        targetUserId: userId,
        requiredPermission: "DELETE_USER",
        attemptedAction: "DELETE_USER",
        timestamp: new Date().toISOString(),
      },
      undefined, // cause
      true // sendTelegramAlert - security violation
    );
  }

  // Additional check: prevent self-deletion
  if (requester.id === userId) {
    throw new ForbiddenError(
      "Cannot delete your own account",
      {
        userId: requester.id,
        attemptedAction: "SELF_DELETE",
      },
      undefined,
      true // sendTelegramAlert - suspicious self-deletion attempt
    );
  }

  // Proceed with deletion...
}

JavaScript:

const { ForbiddenError } = require("@munisn/app-errors");

// Check permissions before sensitive operations
async function deleteUser(userId, requester) {
  // Security check with telegram alert
  if (!requester.hasPermission("DELETE_USER")) {
    throw new ForbiddenError(
      "Insufficient permissions to delete user",
      {
        userId: requester.id,
        targetUserId: userId,
        requiredPermission: "DELETE_USER",
        attemptedAction: "DELETE_USER",
        timestamp: new Date().toISOString(),
      },
      undefined, // cause
      true // sendTelegramAlert - security violation
    );
  }

  // Additional check: prevent self-deletion
  if (requester.id === userId) {
    throw new ForbiddenError(
      "Cannot delete your own account",
      {
        userId: requester.id,
        attemptedAction: "SELF_DELETE",
      },
      undefined,
      true // sendTelegramAlert - suspicious self-deletion attempt
    );
  }

  // Proceed with deletion...
}

📚 API Reference

Base Class: AppError

Abstract base class for all application errors.

abstract class AppError extends Error {
  readonly code: ErrorCode | string;
  readonly severity: ErrorSeverity;
  readonly category: ErrorCategory;
  readonly statusCode?: number;
  readonly isOperational: boolean;
  readonly metadata?: Record<string, unknown>;
  readonly sendTelegramAlert?: boolean;

  toJSON(): Record<string, unknown>;
  toEvent(context?: ErrorEventContext): ErrorEvent;
  toString(): string;
}

Error Classes

| Error Class | Code | Severity | Category | Status Code | Default Telegram Alert | Use Case | | ---------------------- | ------------------------ | ---------- | ------------ | ----------- | ---------------------- | ----------------------------- | | ValidationError | VALIDATION_ERROR | LOW | VALIDATION | 400 | false | Input validation failures | | BusinessLogicError | BUSINESS_LOGIC_ERROR | MEDIUM | BUSINESS | 422 | false | Domain rule violations | | DatabaseError | DATABASE_ERROR | HIGH | INFRA | 500 | false | Database operation failures | | ExternalServiceError | EXTERNAL_SERVICE_ERROR | HIGH | INFRA | 502 | false | External API/service failures | | UnauthorizedError | UNAUTHORIZED | MEDIUM | SECURITY | 401 | false | Authentication failures | | ForbiddenError | FORBIDDEN | MEDIUM | SECURITY | 403 | false | Authorization failures | | NotFoundError | NOT_FOUND | LOW | BUSINESS | 404 | false | Resource not found | | InternalServerError | INTERNAL_SERVER_ERROR | CRITICAL | UNKNOWN | 500 | true | Unexpected errors |

Note: All error classes accept an optional sendTelegramAlert parameter (default: false, except InternalServerError which defaults to true).

Enums

ErrorSeverity

enum ErrorSeverity {
  LOW = "LOW",
  MEDIUM = "MEDIUM",
  HIGH = "HIGH",
  CRITICAL = "CRITICAL",
}

ErrorCategory

enum ErrorCategory {
  VALIDATION = "VALIDATION",
  BUSINESS = "BUSINESS",
  INFRA = "INFRA",
  SECURITY = "SECURITY",
  UNKNOWN = "UNKNOWN",
}

ErrorCode

Machine-readable error codes. See src/enums/ErrorCode.ts for the complete list.

Enviroment

enum Enviroment {
  DEVELOPMENT = "DEVELOPMENT",
  PRODUCTION = "PRODUCTION",
  TEST = "TEST",
}

Interfaces

ErrorEvent

Standard event payload for publishing to the error microservice (NATS, Kafka, etc.).

interface ErrorEvent {
  api_name: string;
  service_name: string;
  endpoint: string;
  http_method: string;
  status_code: number;
  severity: ErrorSeverity;
  enviroment: Enviroment | string;
  send_telegram_alert: boolean;
  error_code: string;
  error_message: string;
  error_stack?: string;
  request_payload?: Record<string, unknown>;
  user_id?: string | number | null;
  client_ip?: string;
  metadata?: Record<string, unknown>;
  timestamp: string; // ISO 8601
}

ErrorEventContext

Context information for enriching error events with HTTP request data.

interface ErrorEventContext {
  api_name?: string;
  service_name?: string;
  endpoint?: string;
  http_method?: string;
  user_id?: string | number | null;
  client_ip?: string;
  request_payload?: Record<string, unknown>;
  enviroment?: Enviroment | string;
}

🔧 Examples

Express (JavaScript)

See examples/express-javascript/ for a complete example.

const { ValidationError } = require("@munisn/app-errors");

app.post("/api/users", (req, res, next) => {
  try {
    if (!req.body.email) {
      throw new ValidationError("Email is required", { field: "email" });
    }
    // ... rest of logic
  } catch (error) {
    next(error);
  }
});

Express (TypeScript)

See examples/express-typescript/ for a complete example.

import { Request, Response, NextFunction } from "express";
import { ValidationError, ForbiddenError } from "@munisn/app-errors";

app.post("/api/users", (req: Request, res: Response, next: NextFunction) => {
  try {
    // Basic validation (no telegram alert)
    if (!req.body.email) {
      throw new ValidationError("Email is required", { field: "email" });
    }

    // Validation with telegram alert for suspicious patterns
    if (containsSQLInjection(req.body.email)) {
      throw new ValidationError(
        "Invalid email format",
        { field: "email", value: req.body.email, suspicious: true },
        undefined,
        true // sendTelegramAlert - potential security threat
      );
    }

    // Security check with telegram alert
    if (!req.user.hasPermission("CREATE_USER")) {
      throw new ForbiddenError(
        "Insufficient permissions",
        { userId: req.user.id, action: "CREATE_USER" },
        undefined,
        true // sendTelegramAlert - security violation
      );
    }

    // ... rest of logic
  } catch (error) {
    next(error);
  }
});

NestJS

See examples/nestjs/ for a complete example.

import { ValidationError } from "@munisn/app-errors";

@Controller("api/users")
export class UsersController {
  @Post()
  createUser(@Body() body: { email: string }) {
    if (!body.email) {
      throw new ValidationError("Email is required", { field: "email" });
    }
    // ... rest of logic
  }
}

📋 Error Event Example

When you call error.toEvent(context), you get a standardized event payload:

import {
  ValidationError,
  ErrorEventContext,
  Enviroment,
} from "@munisn/app-errors";

const error = new ValidationError(
  "Email is required",
  { field: "email" },
  undefined,
  false
);

const context: ErrorEventContext = {
  api_name: "users-api",
  service_name: "users-service",
  endpoint: "/api/users",
  http_method: "POST",
  user_id: "123",
  client_ip: "192.168.1.1",
  request_payload: { email: "", name: "John" },
  enviroment: Enviroment.PRODUCTION,
};

const event = error.toEvent(context);

Resulting event:

{
  "api_name": "users-api",
  "service_name": "users-service",
  "endpoint": "/api/users",
  "http_method": "POST",
  "status_code": 400,
  "severity": "LOW",
  "enviroment": "production",
  "send_telegram_alert": false,
  "error_code": "VALIDATION_ERROR",
  "error_message": "Email is required",
  "error_stack": "ValidationError: Email is required\n    at ...",
  "request_payload": {
    "email": "",
    "name": "John"
  },
  "user_id": "123",
  "client_ip": "192.168.1.1",
  "metadata": {
    "field": "email"
  },
  "timestamp": "2024-01-15T10:30:00.000Z"
}

🏗️ Architecture

Project Structure

src/
├── errors/
│   ├── AppError.ts              # Base error class
│   ├── ValidationError.ts
│   ├── BusinessLogicError.ts
│   ├── DatabaseError.ts
│   ├── ExternalServiceError.ts
│   ├── UnauthorizedError.ts
│   ├── ForbiddenError.ts
│   ├── NotFoundError.ts
│   └── InternalServerError.ts
├── enums/
│   ├── ErrorCode.ts
│   ├── ErrorSeverity.ts
│   └── ErrorCategory.ts
├── interfaces/
│   └── ErrorEvent.ts
└── index.ts                      # Public API

Design Principles

  1. Framework Agnostic: No dependencies on Express, NestJS, or any HTTP framework
  2. Infrastructure Agnostic: No dependencies on NATS, Kafka, or logging libraries
  3. Serializable: All errors can be converted to JSON and events
  4. Type Safe: Full TypeScript support with proper types
  5. Extensible: Easy to create custom error classes extending AppError

🔄 Error Chaining

Supports ES2022 error chaining via the cause parameter:

try {
  await database.query("SELECT * FROM users");
} catch (dbError) {
  throw new DatabaseError(
    "Failed to fetch users",
    { query: "SELECT * FROM users" },
    dbError, // cause
    true // sendTelegramAlert for database errors
  );
}

🎨 Creating Custom Errors

Extend AppError to create custom error types:

import { AppError } from "@munisn/app-errors";
import { ErrorCode, ErrorSeverity, ErrorCategory } from "@munisn/app-errors";

export class PaymentProcessingError extends AppError {
  constructor(
    message: string = "Payment processing failed",
    metadata?: Record<string, unknown>,
    cause?: Error,
    sendTelegramAlert: boolean = true
  ) {
    super(
      ErrorCode.EXTERNAL_SERVICE_ERROR, // or a custom code
      message,
      ErrorSeverity.HIGH,
      ErrorCategory.INFRA,
      502,
      metadata,
      true,
      cause,
      sendTelegramAlert
    );
  }
}

📝 License

MIT

🤝 Contributing

This is an internal library. For issues or contributions, please contact the maintainers.


Version: 1.0.0
Stability: Stable API for production use