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

error-express

v1.3.1

Published

A lightweight, zero-dependency package to handle global errors in Express.js applications. It catches unhandled errors and provides a simple, yet powerful way to manage them.

Readme

error-express

Instant error handling for Express.js. Catch errors automatically and respond with clean, consistent JSON errors. No manual try-catch blocks needed!

Why error-express?

Traditional Express error handling is tedious:

  • You need to wrap routes in try-catch blocks
  • You have to manually call next(error)
  • You need to handle both sync and async errors
  • Every error response needs to be formatted consistently

error-express handles all of this automatically. Just throw an error and it's caught, formatted, and sent as JSON.

Install

npm install error-express

Or use your preferred package manager:

yarn add error-express
pnpm add error-express
bun add error-express

Quick Start

1. Add the error handler to your Express app:

const express = require("express");
const { globalErrorHandler, ServerError, HttpStatusCodes } = require("error-express");

const app = express();

// Your routes here
app.get("/api/users", (req, res) => {
  throw new ServerError("Somthing Error.", HttpStatusCodes.BAD_REQUEST);  // custom message and status code

  res.json({ message: "success" });
});

// Add this at the end, after all routes
app.use(globalErrorHandler);

app.listen(3000);

2. Throw errors in your routes (that's it!):

// No try-catch needed!
app.get("/api/users/:id", (req, res, next) => {
  const user = null; // Assume user not found

  if (!user) {
    throw new NotFoundError("User not found");  // custom message
  }

  res.json(user);
});

Errors are automatically caught and returned as JSON.

How It Works

error-express wraps your route handlers to automatically catch:

  • ✅ Thrown errors (synchronous)
  • ✅ Promise rejections (async/await)
  • ✅ Callback errors (via next())
  • ✅ Unhandled exceptions

All errors are formatted consistently without you writing try-catch.

Features

  • No Try-Catch Needed: Automatic error catching in sync and async code
  • Automatic Error Catching: Catches thrown errors in routes and middleware
  • Consistent JSON Responses: All errors return a standardized format
  • Custom HTTP Status Codes: Set the right status code for each error
  • Sync/Async Support: Works with synchronous and asynchronous code without manual handling
  • Extendable: Create custom error types by extending AppError
  • Clean Code: Write business logic, not error handling boilerplate
  • 20+ Pre-built Error Classes: Ready-to-use for common HTTP scenarios (400, 401, 404, 429, etc.)
  • Type-Safe: Full TypeScript support with proper type definitions
  • Custom Error Handlers: Extend error handling for logging, monitoring, and Sentry integration

API

Built-in Error Classes

All error classes come pre-configured with correct HTTP status codes and default messages. Use them directly without manual setup:

Client Errors (4xx)

// 400 Bad Request
throw new BadRequestError("Invalid input format");

// 401 Unauthorized
throw new UnauthorizedError("Missing authentication token");

// 403 Forbidden
throw new ForbiddenError("You don't have permission");

// 404 Not Found
throw new NotFoundError("Resource not found");

// 405 Method Not Allowed
throw new MethodNotAllowedError();

// 406 Not Acceptable
throw new NotAcceptableError();

// 408 Request Timeout
throw new RequestTimeoutError();

// 409 Conflict
throw new ConflictError("Email already exists");

// 413 Payload Too Large
throw new PayloadTooLargeError("File size exceeds limit");

// 415 Unsupported Media Type
throw new UnsupportedMediaTypeError("Only JSON is accepted");

// 422 Unprocessable Entity
throw new UnprocessableEntityError("Validation failed");

// 429 Too Many Requests
throw new TooManyRequestsError("Rate limit exceeded");

// 402 Payment Required
throw new PaymentRequiredError("Subscription required");

// 410 Gone
throw new GoneError("Resource permanently deleted");

// 412 Precondition Failed
throw new PreconditionFailedError();

Server Errors (5xx)

// 500 Internal Server Error (default)
throw new ServerError("Something went wrong");

// 501 Not Implemented
throw new NotImplementedError("Feature coming soon");

// 502 Bad Gateway
throw new BadGatewayError();

// 503 Service Unavailable
throw new ServiceUnavailableError("Maintenance in progress");

// 504 Gateway Timeout
throw new GatewayTimeoutError();

// 507 Insufficient Storage
throw new InsufficientStorageError();

Response Format

All errors return a consistent JSON format:

{
  "status": "error",
  "statusCode": 404,
  "message": "Resource not found"
}

ServerError - Custom Status Code

For flexibility, use ServerError with any custom status code:

throw new ServerError("Something went wrong", HttpStatusCodes.INTERNAL_SERVER_ERROR); // 500
throw new ServerError("Not found", HttpStatusCodes.NOT_FOUND); // 404
throw new ServerError("Invalid input", HttpStatusCodes.BAD_REQUEST); // 400

AppError - Base Class

All error classes extend AppError. You can also extend it for custom errors:

class CustomValidationError extends AppError {
  constructor(message, field) {
    super(message, 400, true);
    this.field = field;
  }
}

throw new CustomValidationError("Invalid email", "email");

globalErrorHandler

The middleware that catches and formats all errors. Always add it last:

app.use(globalErrorHandler);

Benefits

1. Zero Try-Catch Boilerplate

Write clean async code without wrapping everything in try-catch:

// ❌ Before (verbose)
app.post("/api/users", async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.json(user);
  } catch (err) {
    next(err);
  }
});

// ✅ After (clean)
app.post("/api/users", async (req, res, next) => {
  const user = await User.create(req.body);
  res.json(user);
});

2. Consistent Error Format

Every error returns the same structure automatically, improving frontend integration.

3. DRY Principle

Use pre-built error classes instead of creating custom responses:

// ✅ One-liner, reusable across your app
throw new NotFoundError("User not found");
throw new UnauthorizedError();
throw new TooManyRequestsError("Rate limited");

4. Type Safety

Full TypeScript support with proper error typing for better IDE autocomplete.

5. Automatic Handling

All these are caught without manual try-catch:

  • ✅ Thrown errors (sync)
  • ✅ Promise rejections (async)
  • ✅ Callback errors
  • ✅ Middleware errors

Examples

❌ Without error-express (Traditional Way)

// Manual try-catch, manual next(), manual formatting
app.post("/api/users", async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.json(user);
  } catch (err) {
    next(new ServerError("Failed to create user", 500));
  }
});

✅ With error-express (Our Way)

// Just throw the error! No try-catch needed
app.post("/api/users", async (req, res, next) => {
  const user = await User.create(req.body);
  res.json(user);
});

// Errors are caught and formatted automatically

Validation Error (No Try-Catch)

app.post("/api/signup", (req, res, next) => {
  const { email, password } = req.body;

  if (!email) {
    throw new BadRequestError("Email is required");
  }

  if (password.length < 6) {
    throw new BadRequestError("Password must be at least 6 characters");
  }

  res.json({ success: true });
});

Async Errors (Automatically Caught)

// Even with async, errors are caught automatically!
app.get("/api/data", async (req, res, next) => {
  const data = await fetchFromDatabase(); // If this fails, it's caught
  const parsed = JSON.parse(data); // If this fails, it's caught
  res.json(parsed);
});

Authentication Middleware

const checkAuth = (req, res, next) => {
  const token = req.headers.authorization;

  if (!token) {
    throw new UnauthorizedError("Missing authentication token");
  }

  try {
    const decoded = verifyToken(token);
    req.user = decoded;
    next();
  } catch (err) {
    throw new UnauthorizedError("Invalid token");
  }
};

app.get("/api/protected", checkAuth, (req, res) => {
  res.json({ data: "secret", user: req.user });
});

Rate Limiting

app.post("/api/login", (req, res, next) => {
  const attempts = getLoginAttempts(req.ip);

  if (attempts > 5) {
    throw new TooManyRequestsError("Too many login attempts. Try again later.");
  }

  // Login logic here
  res.json({ token: "..." });
});

File Upload Validation

app.post("/api/upload", (req, res, next) => {
  const fileSize = req.headers["content-length"];

  if (fileSize > 10 * 1024 * 1024) {
    throw new PayloadTooLargeError("File must be under 10MB");
  }

  const contentType = req.headers["content-type"];
  if (!contentType?.includes("application/json")) {
    throw new UnsupportedMediaTypeError("Only JSON is accepted");
  }

  res.json({ success: true });
});

Custom Error Handling

Create Custom Error Classes

Extend AppError for domain-specific errors:

import { AppError } from "error-express";

class ValidationError extends AppError {
  constructor(message, field) {
    super(message, 400, true);
    this.field = field;
  }
}

class DatabaseError extends AppError {
  constructor(message) {
    super(message, 500, true);
  }
}

// Usage
app.post("/api/users", (req, res, next) => {
  if (!req.body.email) {
    throw new ValidationError("Email is required", "email");
  }
  res.json({ success: true });
});

Custom Global Error Handler

Extend the error handler for logging, monitoring, or custom responses:

import { handleError, AppError } from "error-express";
import { Request, Response, NextFunction } from "express";

// Custom handler with logging
const customErrorHandler = (
  err: Error | AppError,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const errorResponse = handleError(err);

  // Log to external service
  console.error({
    timestamp: new Date().toISOString(),
    path: req.path,
    method: req.method,
    statusCode: errorResponse.statusCode,
    message: errorResponse.message,
    stack: process.env.NODE_ENV === "development" ? err.stack : undefined,
  });

  // Send response
  res.status(errorResponse.statusCode).json({
    status: errorResponse.status,
    statusCode: errorResponse.statusCode,
    message: errorResponse.message,
    // Add custom fields
    timestamp: new Date().toISOString(),
    path: req.path,
  });
};

app.use(customErrorHandler);

Add Monitoring & Sentry Integration

import * as Sentry from "@sentry/node";
import { handleError, AppError } from "error-express";

const monitoredErrorHandler = (
  err: Error | AppError,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // Capture in Sentry
  if (err instanceof AppError && !err.isOparational) {
    Sentry.captureException(err);
  }

  const errorResponse = handleError(err);
  res.status(errorResponse.statusCode).json({
    status: errorResponse.status,
    statusCode: errorResponse.statusCode,
    message: errorResponse.message,
  });
};

app.use(monitoredErrorHandler);

Real-World Example

Complete server with authentication, validation, and error handling:

const express = require("express");
const {
  globalErrorHandler,
  BadRequestError,
  NotFoundError,
  UnauthorizedError,
  TooManyRequestsError,
} = require("error-express");

const app = express();
app.use(express.json());

// Auth middleware
const auth = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) throw new UnauthorizedError();
  req.user = verifyToken(token);
  next();
};

// Rate limiting middleware
const rateLimit = (req, res, next) => {
  if (exceedsRateLimit(req.ip)) {
    throw new TooManyRequestsError("Rate limited");
  }
  next();
};

// Routes - clean and simple!
app.post("/api/users", rateLimit, async (req, res) => {
  if (!req.body.email) throw new BadRequestError("Email required");
  const user = await User.create(req.body);
  res.status(201).json(user);
});

app.get("/api/users/:id", async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) throw new NotFoundError("User not found");
  res.json(user);
});

app.delete("/api/users/:id", auth, async (req, res) => {
  const user = await User.findByIdAndDelete(req.params.id);
  if (!user) throw new NotFoundError("User not found");
  res.json({ deleted: true });
});

// Error handling - ONE line!
app.use(globalErrorHandler);

app.listen(3000);

Support

Found a bug? Open an issue on GitHub

Author

Shakil Ahmed (@sa3akash)

License

MIT License - See LICENSE file for details