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

express-utility-kit

v1.1.0

Published

Production-ready utilities for Express applications to reduce boilerplate

Readme

express-utility-kit

Production-ready utilities for Express applications to reduce boilerplate and standardize error handling, async operations, and API responses.

Installation

npm install express-utility-kit

Features

  • asyncHandler: Wrap async route handlers to automatically catch errors
  • AppError: Custom error class with status codes and operational flags
  • errorMiddleware: Global error handler with standardized JSON responses
  • notFoundMiddleware: Handle undefined routes with 404 errors
  • responseFormatter: Helper methods for consistent API responses with pagination support
  • authenticate: JWT authentication middleware
  • JWT Helpers: Generate, verify, and decode JWT tokens
  • Password Helpers: Hash and compare passwords using bcrypt
  • Pagination Helpers: Calculate pagination metadata and parse query parameters
  • Validation Helpers: Email validation, password strength validation, and input sanitization
  • OTP Helpers: Generate numeric/alphanumeric OTPs with expiry, secure tokens

Usage

Basic Setup

import express from 'express';
import {
  asyncHandler,
  errorMiddleware,
  notFoundMiddleware,
  AppError,
  responseFormatter
} from 'express-utility-kit';

const app = express();

// Apply response formatter middleware globally
app.use(responseFormatter);

// Your routes here
app.get('/api/users', asyncHandler(async (req, res) => {
  const users = await getUsersFromDB();
  res.success(users, 'Users fetched successfully');
}));

app.post('/api/users', asyncHandler(async (req, res) => {
  const user = await createUser(req.body);
  res.success(user, 'User created successfully', 201);
}));

// Handle undefined routes (must be after all routes)
app.use(notFoundMiddleware);

// Global error handler (must be last)
app.use(errorMiddleware);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Using asyncHandler

Wrap async route handlers to automatically catch errors and pass them to error middleware:

import { asyncHandler } from 'express-utility-kit';

app.get('/api/posts/:id', asyncHandler(async (req, res) => {
  const post = await Post.findById(req.params.id);
  
  if (!post) {
    throw new AppError('Post not found', 404);
  }
  
  res.success(post, 'Post retrieved successfully');
}));

Using AppError

Throw custom errors with specific status codes:

import { AppError, asyncHandler } from 'express-utility-kit';

app.delete('/api/posts/:id', asyncHandler(async (req, res) => {
  const post = await Post.findById(req.params.id);
  
  if (!post) {
    throw new AppError('Post not found', 404);
  }
  
  if (post.userId !== req.user.id) {
    throw new AppError('Not authorized to delete this post', 403);
  }
  
  await post.remove();
  res.success(null, 'Post deleted successfully');
}));

Using responseFormatter

The response formatter adds helper methods to the response object:

// Success response
res.success(data, message, statusCode);

// Example:
res.success({ id: 1, name: 'John' }, 'User created', 201);
// Returns:
// {
//   "success": true,
//   "message": "User created",
//   "data": { "id": 1, "name": "John" }
// }

// Error response
res.error(message, statusCode);

// Example:
res.error('Invalid credentials', 401);
// Returns:
// {
//   "success": false,
//   "message": "Invalid credentials",
//   "data": null
// }

// Paginated response
res.paginated(data, pagination, message, statusCode);

// Example:
res.paginated(
  users,
  { page: 1, limit: 10, total: 50 },
  'Users retrieved successfully'
);
// Returns:
// {
//   "success": true,
//   "message": "Users retrieved successfully",
//   "data": [...users],
//   "pagination": {
//     "page": 1,
//     "limit": 10,
//     "total": 50,
//     "totalPages": 5,
//     "hasNextPage": true,
//     "hasPrevPage": false
//   }
// }

Using JWT Authentication

import { generateToken, verifyToken, authenticate } from 'express-utility-kit';

// Generate token on login
app.post('/api/login', asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  
  // Verify user credentials (your logic here)
  const user = await User.findOne({ email });
  
  if (!user || !(await comparePassword(password, user.password))) {
    throw new AppError('Invalid credentials', 401);
  }
  
  // Generate JWT token
  const token = generateToken(
    { id: user.id, email: user.email },
    process.env.JWT_SECRET,
    { expiresIn: '7d' }
  );
  
  res.success({ token, user }, 'Login successful');
}));

// Protect routes with authentication middleware
const authMiddleware = authenticate(process.env.JWT_SECRET);

app.get('/api/profile', authMiddleware, asyncHandler(async (req, res) => {
  // req.user contains decoded token payload
  const user = await User.findById(req.user.id);
  res.success(user, 'Profile retrieved');
}));

Using Password Helpers

import { hashPassword, comparePassword } from 'express-utility-kit';

// Register user with hashed password
app.post('/api/register', asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  
  // Hash password before saving
  const hashedPassword = await hashPassword(password);
  
  const user = await User.create({
    email,
    password: hashedPassword
  });
  
  res.success(user, 'User registered successfully', 201);
}));

// Login with password comparison
app.post('/api/login', asyncHandler(async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  
  if (!user || !(await comparePassword(password, user.password))) {
    throw new AppError('Invalid credentials', 401);
  }
  
  res.success({ user }, 'Login successful');
}));

Using Pagination Helpers

import { parsePaginationQuery, getPaginationMeta } from 'express-utility-kit';

app.get('/api/users', asyncHandler(async (req, res) => {
  // Parse pagination from query params (?page=1&limit=10)
  const { page, limit, skip } = parsePaginationQuery(req.query);
  
  // Fetch data with pagination
  const users = await User.find().skip(skip).limit(limit);
  const total = await User.countDocuments();
  
  // Send paginated response
  res.paginated(users, { page, limit, total }, 'Users retrieved');
}));

Using Validation Helpers

import { isValidEmail, validatePassword, sanitizeString } from 'express-utility-kit';

app.post('/api/register', asyncHandler(async (req, res) => {
  const { email, password, name } = req.body;
  
  // Validate email
  if (!isValidEmail(email)) {
    throw new AppError('Invalid email format', 400);
  }
  
  // Validate password strength
  const passwordValidation = validatePassword(password, {
    minLength: 8,
    requireUppercase: true,
    requireNumbers: true
  });
  
  if (!passwordValidation.isValid) {
    throw new AppError(passwordValidation.errors.join(', '), 400);
  }
  
  // Sanitize user input
  const sanitizedName = sanitizeString(name);
  
  // Create user...
  res.success({ email, name: sanitizedName }, 'User registered', 201);
}));

Using OTP Helpers

import { 
  generateOTP, 
  generateAlphanumericOTP, 
  generateOTPWithExpiry,
  isOTPExpired,
  generateSecureToken
} from 'express-utility-kit';

// Generate numeric OTP
app.post('/api/send-otp', asyncHandler(async (req, res) => {
  const { email } = req.body;
  
  // Generate 6-digit OTP
  const otp = generateOTP(6);
  
  // Or generate with expiry
  const { otp: otpCode, expiresAt } = generateOTPWithExpiry(6, 5); // 5 minutes
  
  // Save OTP to database
  await OTP.create({
    email,
    otp: otpCode,
    expiresAt
  });
  
  // Send OTP via email/SMS (your logic)
  await sendOTPEmail(email, otpCode);
  
  res.success(null, 'OTP sent successfully');
}));

// Verify OTP
app.post('/api/verify-otp', asyncHandler(async (req, res) => {
  const { email, otp } = req.body;
  
  const otpRecord = await OTP.findOne({ email, otp });
  
  if (!otpRecord) {
    throw new AppError('Invalid OTP', 400);
  }
  
  if (isOTPExpired(otpRecord.expiresAt)) {
    throw new AppError('OTP has expired', 400);
  }
  
  // OTP is valid
  await OTP.deleteOne({ _id: otpRecord._id });
  
  res.success(null, 'OTP verified successfully');
}));

// Generate alphanumeric OTP
const alphaOTP = generateAlphanumericOTP(8, {
  includeUppercase: true,
  includeLowercase: true,
  includeNumbers: true,
  excludeSimilar: true // Excludes 0, O, I, l, 1
});

// Generate secure token for password reset
app.post('/api/forgot-password', asyncHandler(async (req, res) => {
  const { email } = req.body;
  
  const user = await User.findOne({ email });
  if (!user) {
    throw new AppError('User not found', 404);
  }
  
  // Generate secure token
  const resetToken = generateSecureToken(32);
  const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour
  
  // Save token to database
  user.resetToken = resetToken;
  user.resetTokenExpiry = expiresAt;
  await user.save();
  
  // Send reset link via email
  const resetLink = `https://yourapp.com/reset-password?token=${resetToken}`;
  await sendPasswordResetEmail(email, resetLink);
  
  res.success(null, 'Password reset link sent');
}));

Complete Example

import express from 'express';
import {
  asyncHandler,
  errorMiddleware,
  notFoundMiddleware,
  AppError,
  responseFormatter
} from 'express-utility-kit';

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

// Mock database
const users = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob', email: '[email protected]' }
];

// Get all users
app.get('/api/users', asyncHandler(async (req, res) => {
  res.success(users, 'Users retrieved successfully');
}));

// Get user by ID
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    throw new AppError('User not found', 404);
  }
  
  res.success(user, 'User found');
}));

// Create user
app.post('/api/users', asyncHandler(async (req, res) => {
  const { name, email } = req.body;
  
  if (!name || !email) {
    throw new AppError('Name and email are required', 400);
  }
  
  const newUser = {
    id: users.length + 1,
    name,
    email
  };
  
  users.push(newUser);
  res.success(newUser, 'User created successfully', 201);
}));

// Handle 404
app.use(notFoundMiddleware);

// Global error handler
app.use(errorMiddleware);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

API Reference

Middleware

asyncHandler(fn)

Wraps an async function to catch errors automatically.

  • Parameters: fn (Function) - Async route handler
  • Returns: Express middleware function

authenticate(secret, options)

JWT authentication middleware factory.

  • Parameters:
    • secret (String) - JWT secret key
    • options (Object) - Optional configuration
      • headerKey (String) - Header to read token from (default: 'authorization')
      • tokenPrefix (String) - Token prefix (default: 'Bearer')
      • attachTo (String) - Property name to attach decoded token (default: 'user')
  • Returns: Express middleware function

errorMiddleware(err, req, res, next)

Global error handling middleware.

  • Sends standardized JSON error responses
  • Includes stack trace in development mode (NODE_ENV=development)

notFoundMiddleware(req, res, next)

Handles undefined routes with 404 errors.

  • Should be placed after all route definitions
  • Creates an AppError with 404 status

responseFormatter(req, res, next)

Adds helper methods to response object:

  • res.success(data, message, statusCode) - Send success response
  • res.error(message, statusCode) - Send error response
  • res.paginated(data, pagination, message, statusCode) - Send paginated response

Error Classes

AppError(message, statusCode)

Custom error class for operational errors.

  • Parameters:
    • message (String) - Error message
    • statusCode (Number) - HTTP status code
  • Properties:
    • status - "fail" for 4xx, "error" for 5xx
    • isOperational - Always true for AppError instances

JWT Utilities

generateToken(payload, secret, options)

Generate JWT token.

  • Parameters:
    • payload (Object) - Data to encode
    • secret (String) - JWT secret key
    • options (Object) - JWT options (expiresIn, etc.)
  • Returns: JWT token string

verifyToken(token, secret)

Verify JWT token.

  • Parameters:
    • token (String) - JWT token
    • secret (String) - JWT secret key
  • Returns: Decoded token payload
  • Throws: Error if invalid or expired

decodeToken(token)

Decode JWT without verification.

  • Parameters: token (String) - JWT token
  • Returns: Decoded token payload

Password Utilities

hashPassword(password, saltRounds)

Hash password using bcrypt.

  • Parameters:
    • password (String) - Plain text password
    • saltRounds (Number) - Salt rounds (default: 10)
  • Returns: Promise - Hashed password

comparePassword(password, hash)

Compare password with hash.

  • Parameters:
    • password (String) - Plain text password
    • hash (String) - Hashed password
  • Returns: Promise - True if matches

Pagination Utilities

parsePaginationQuery(query, defaults)

Parse pagination from query parameters.

  • Parameters:
    • query (Object) - Request query object
    • defaults (Object) - Default values
  • Returns: Object with page, limit, skip

getPaginationMeta(page, limit, total)

Calculate pagination metadata.

  • Parameters:
    • page (Number) - Current page
    • limit (Number) - Items per page
    • total (Number) - Total items
  • Returns: Pagination metadata object

Validation Utilities

isValidEmail(email)

Validate email format.

  • Parameters: email (String)
  • Returns: Boolean

validatePassword(password, options)

Validate password strength.

  • Parameters:
    • password (String)
    • options (Object) - Validation rules
  • Returns: Object with isValid and errors array

sanitizeString(input)

Sanitize string input.

  • Parameters: input (String)
  • Returns: Sanitized string

OTP Utilities

generateOTP(length)

Generate numeric OTP.

  • Parameters: length (Number) - OTP length 4-10 (default: 6)
  • Returns: String - Numeric OTP

generateAlphanumericOTP(length, options)

Generate alphanumeric OTP.

  • Parameters:
    • length (Number) - OTP length 4-20 (default: 6)
    • options (Object) - Generation options
      • includeUppercase (Boolean) - Include uppercase letters (default: true)
      • includeLowercase (Boolean) - Include lowercase letters (default: true)
      • includeNumbers (Boolean) - Include numbers (default: true)
      • excludeSimilar (Boolean) - Exclude similar characters like 0,O,I,l,1 (default: true)
  • Returns: String - Alphanumeric OTP

generateOTPWithExpiry(length, expiryMinutes)

Generate OTP with expiry time.

  • Parameters:
    • length (Number) - OTP length (default: 6)
    • expiryMinutes (Number) - Expiry time in minutes (default: 5)
  • Returns: Object with otp, expiresAt, expiryMinutes

isOTPExpired(expiresAt)

Check if OTP is expired.

  • Parameters: expiresAt (Date|String) - Expiry timestamp
  • Returns: Boolean - True if expired

generateSecureToken(bytes)

Generate secure random token for email verification, password reset, etc.

  • Parameters: bytes (Number) - Number of bytes (default: 32)
  • Returns: String - Hex string token

Environment Variables

  • NODE_ENV - Set to "development" to include stack traces in error responses

License

MIT