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

devdad-express-utils

v1.6.0

Published

Reusable Express.js utilities for error handling, async wrapping, and more

Downloads

1,070

Readme

Express Utils

A collection of reusable utilities for Express.js applications, including error handling, async route wrapping, custom error classes, MongoDB connection management, and Winston-based logging.

Installation

npm install devdad-express-utils
# or
yarn add devdad-express-utils
# or
pnpm add devdad-express-utils

Usage

Error Handling

import express from "express";
import { errorHandler, AppError } from "devdad-express-utils";

const app = express();

// Your routes here

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

// In your controllers, throw AppError for operational errors
throw new AppError("Something went wrong", 400);

Async Route Wrapping

import { catchAsync } from "devdad-express-utils";

const getUsers = catchAsync(async (req, res, next) => {
  const users = await User.find();
  res.json(users);
});

app.get("/users", getUsers);

Custom Error Class

import { AppError } from "devdad-express-utils";

throw new AppError("Validation failed", 400, ["Email is required"]);

Response Formatting

Standardize API responses with consistent JSON structure.

import { sendSuccess, sendError, sendPaginated } from "devdad-express-utils";

// Success response
sendSuccess(res, { id: 1, name: "John" }, "User fetched", 200);

// Chain additional response methods
sendSuccess(res, { id: 1, name: "John" })
  .cookie("session", "abc123")
  .setHeader("X-Custom", "value");

// Error response
sendError(res, "User not found", 404);

Database Connection

MongoDB connection utility with automatic reconnection, exponential backoff, and configurable retry logic.

import { connectDB, getDBStatus, resetDBConnection } from "devdad-express-utils";

// Connect to MongoDB (ensure MONGO_URI is set in environment)
await connectDB();

// Check connection status
const status = getDBStatus();
console.log(status); // { isConnected: true, readyState: 1, host: '...', name: '...', retryCount: 0 }

// Manually reset and retry (useful after Docker container restarts)
await resetDBConnection();

Environment Variables

  • MONGO_URI: MongoDB connection string (required)
  • DB_MAX_RETRIES: Maximum connection retry attempts (default: 10)
  • DB_RETRY_INTERVAL: Initial retry interval in milliseconds (default: 3000)
  • NODE_ENV: Set to 'production' to exit after max retries, 'development' to keep process alive

Retry Behavior

  • Exponential backoff: Retry intervals increase exponentially (3s → 6s → 12s → 24s → 30s max)
  • Random jitter: Adds up to 1 second of random delay to prevent thundering herd
  • Docker-friendly: Higher default retry count (10) accommodates container startup times
  • Development mode: Process stays alive after max retries for manual recovery
  • Production mode: Process exits after max retries to allow container restart

Logging

Winston-based logger with configurable service name and environment-aware transports.

import { logger } from "devdad-express-utils";

// Log messages at different levels
logger.info("User logged in", { userId: 123 });
logger.error("Database connection failed", { error: err.message });
logger.debug("Processing request", { requestId: "abc-123" });

Configuration

  • Service Name: Set SERVICE_NAME environment variable to customize the service name in logs (defaults to "express-utils")
  • Log Level: "debug" in development, "info" in production
  • Transports:
    • Development: Console (colored) + error.log + combined.log files
    • Production: Console only (suitable for platforms like Railway)

Log Files

In development, logs are written to:

  • error.log: Error level and above
  • combined.log: All log levels

Error Handling Patterns

Using AppError

For operational errors (expected errors like validation):

// In controllers wrapped with catchAsync
const createUser = catchAsync(async (req, res, next) => {
  // Validation fails
  return next(new AppError("Email is required", 400));
});

// Or for unexpected errors
throw new AppError("Database connection failed", 500);

Why next(new AppError()) over throw?

  • next() passes the error to your error handler middleware
  • Allows centralized error handling and formatting
  • Better for Express middleware pattern
  • throw is more for unexpected errors that bubble up

Complete Example

const express = require("express");
const { AppError, catchAsync, errorHandler } = require("devdad-express-utils");

const app = express();

const getUser = catchAsync(async (req, res, next) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    return next(new AppError("User not found", 404));
  }
  res.json(user);
});

app.get("/users/:id", getUser);

// Error handler should be last
app.use(errorHandler);

JavaScript Usage

const { AppError, catchAsync, errorHandler } = require("devdad-express-utils");

// Or with ES modules
import { AppError, catchAsync, errorHandler } from "devdad-express-utils";

API

AppError

Custom error class for operational errors.

new AppError(message: string, statusCode: number, errors?: string[])

catchAsync

Higher-order function to wrap async route handlers and catch errors.

catchAsync(fn: (req, res, next) => Promise<any>) => (req, res, next) => void

errorHandler

Express error handling middleware with detailed logging in development.

errorHandler(err: any, req: Request, res: Response, next: NextFunction) => void

sendSuccess

Sends a standardized success response. Returns the Response object for method chaining.

sendSuccess(res: Response, data: any, message?: string, statusCode?: number) => Response

sendError

Sends a standardized error response.

sendError(res: Response, message: string, statusCode?: number, data?: any) => void

connectDB

Connects to MongoDB with retry logic and automatic reconnection.

connectDB() => Promise<void>

getDBStatus

Gets the current MongoDB connection status.

getDBStatus() => { isConnected: boolean; readyState: number; host: string; name: string; retryCount: number; }

resetDBConnection

Manually resets retry count and attempts reconnection. Useful for recovering from Docker container restarts.

resetDBConnection() => Promise<void>

logger

Winston logger instance with JSON formatting, timestamps, and error stack traces.

logger: winston.Logger;

Development

# Install dependencies
pnpm install

# Build the package
pnpm run build

# Publish to npm
npm publish

License

ISC