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

@smmorshed/server-utils

v0.0.1

Published

Professional utility package for Express.js-based Node servers

Readme

Server Utils

CI npm bundle size npm version

A professional utility package for Express.js-based Node servers, written in TypeScript with modern best practices.

Features

  • 📦 Dual ESM + CommonJS support
  • 🛠️ Tree-shakable (only import what you need)
  • 💪 Fully typed with TypeScript
  • ✅ Comprehensive test coverage

Installation

npm install @smmorshed/server-utils

Utilities

Logger

A lightweight, customizable logger for Node.js applications with multiple transport options.

Features

  • Multiple Log Levels: debug, info, warn, error
  • Multiple Transports: console, file, memory
  • Timestamp Support: automatic or custom timestamp formats
  • Context Support: attach persistent contextual data to logs
  • Asynchronous Logging: non-blocking log operations with buffering
  • Customizable Formatters: fully control log output format
  • Tree-shakable: only import the features you need

Basic Usage

import { Logger } from '@smmorshed/server-utils';

// Create with default settings
const logger = new Logger();

// Basic usage
logger.info('Server started on port 3000');
logger.warn('Memory usage high', { memoryUsage: process.memoryUsage() });
logger.error('Database connection failed', { retryCount: 3 });

// Change log level at runtime
logger.setLevel('debug');

Advanced Configuration

import { Logger } from '@smmorshed/server-utils';

// Configure with options
const customLogger = new Logger({
  level: 'debug', // 'debug' | 'info' | 'warn' | 'error'
  timestamp: true, // include timestamps in logs
  formatter: (level, message, meta) => {
    // Custom formatter
    return `${level.toUpperCase()}: ${message} ${meta ? JSON.stringify(meta) : ''}`;
  },
  context: { // Persistent context data for all logs 
    service: 'payment-service',
    version: '1.0.0'
  },
  transports: [
    { type: 'console', colors: true },
    { type: 'file', filePath: 'logs/app.log', maxSize: 10485760, maxFiles: 5 }
  ],
  async: true, // Use non-blocking logging
  bufferSize: 100, // Max number of logs to buffer before flushing
  flushInterval: 1000 // Flush interval in milliseconds
});

Using Multiple Transports

import { Logger } from '@smmorshed/server-utils';

const multiTransportLogger = new Logger({
  // Use multiple transports
  transports: [
    { type: 'console' }, // Console output
    { type: 'file', filePath: 'logs/app.log' }, // File output
    { type: 'memory', maxEntries: 1000 } // In-memory storage (useful for testing)
  ]
});

multiTransportLogger.info('This message goes to all transports');

Context Support for Request Tracking

import { Logger } from '@smmorshed/server-utils';
import { v4 as uuidv4 } from 'uuid';

// Create a base logger
const baseLogger = new Logger();

// In an Express middleware
app.use((req, res, next) => {
  const requestId = uuidv4();
  
  // Create a logger with context for this request
  req.logger = baseLogger.withContext({
    requestId,
    path: req.path,
    method: req.method,
    ip: req.ip
  });
  
  next();
});

// Later in a route handler
app.get('/users', (req, res) => {
  req.logger.info('Getting users', { query: req.query });
  // Log will include requestId, path, method, and ip automatically
  
  // ...process request
});

Express.js Application Example

import express from 'express';
import { Logger } from '@smmorshed/server-utils';

const app = express();
const logger = new Logger({ level: process.env.NODE_ENV === 'production' ? 'info' : 'debug' });

// Request logging middleware
app.use((req, res, next) => {
  const start = Date.now();

  logger.debug('Request received', {
    method: req.method,
    path: req.path,
    query: req.query,
  });

  res.on('finish', () => {
    const duration = Date.now() - start;
    const logLevel = res.statusCode >= 400 ? 'error' : 'info';

    logger[logLevel]('Response sent', {
      method: req.method,
      path: req.path,
      statusCode: res.statusCode,
      duration: `${duration}ms`,
    });
  });

  next();
});

// Route handlers
app.get('/api/users', (req, res) => {
  try {
    logger.debug('Fetching users');
    // Database operations...
    const users = [{ id: 1, name: 'John' }];

    logger.info('Users fetched successfully', { count: users.length });
    res.json(users);
  } catch (error) {
    logger.error('Failed to fetch users', {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined
    });
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Server startup
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  logger.info(`Server started`, { port: PORT, env: process.env.NODE_ENV });
});

Structured JSON Logging

import { Logger } from '@smmorshed/server-utils';

// Create a JSON formatter for structured logging
const logger = new Logger({
  formatter: (level, message, meta) => {
    const logEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      ...meta,
      service: 'payment-api',
      environment: process.env.NODE_ENV,
    };
    return JSON.stringify(logEntry);
  },
});

// Now logs will be in JSON format for easy parsing by log aggregation tools
logger.info('Payment processed', {
  paymentId: '12345',
  amount: 99.99,
  currency: 'USD',
  userId: 'user_abc123',
});

// Output: {"timestamp":"2023-04-16T12:34:56.789Z","level":"info","message":"Payment processed","paymentId":"12345","amount":99.99,"currency":"USD","userId":"user_abc123","service":"payment-api","environment":"production"}

Asynchronous Logging

import { Logger } from '@smmorshed/server-utils';

// Create logger with async logging enabled
const asyncLogger = new Logger({
  async: true,
  bufferSize: 50,      // Flush after 50 log entries
  flushInterval: 2000  // Or flush every 2 seconds
});

// These logs will be buffered and won't block the main thread
for (let i = 0; i < 1000; i++) {
  asyncLogger.info(`Processing item ${i}`);
}

// Make sure to close the logger when your application exits
process.on('SIGTERM', () => {
  asyncLogger.close();  // This will flush any remaining logs
  process.exit(0);
});

API Documentation

Logger

A class that provides leveled logging functionality with customizable formatting and multiple transports.

Constructor

constructor(options?: LoggerOptions)

Parameters:

  • options (optional): Configuration options for the logger

LoggerOptions Interface

interface LoggerOptions {
  level?: LogLevel;           // Minimum log level ('debug', 'info', 'warn', 'error')
  timestamp?: boolean;        // Whether to include timestamps in logs
  formatter?: (level: LogLevel, message: string, meta?: Record<string, unknown>) => string;
  context?: Record<string, unknown>;  // Context data for all logs
  transports?: TransportConfig[];     // Where logs should be sent
  async?: boolean;            // Whether logging should be asynchronous
  bufferSize?: number;        // Buffer size for async logging
  flushInterval?: number;     // Flush interval in ms for async logging
}

Transport Types

The Logger supports the following transport configurations:

// Console Transport
{ 
  type: 'console',
  colors?: boolean       // Whether to use colors (not implemented yet)
}

// File Transport
{
  type: 'file',
  filePath: string,      // Path to log file
  maxSize?: number,      // Max file size before rotation (default: 10MB)
  maxFiles?: number      // Max number of files to keep (default: 5)
}

// Memory Transport (useful for testing)
{
  type: 'memory',
  maxEntries?: number    // Max log entries to keep in memory (default: 1000)
}

Methods

debug(message, meta?)
debug(message: string, meta?: Record<string, unknown>): void

Logs a debug message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
info(message, meta?)
info(message: string, meta?: Record<string, unknown>): void

Logs an info message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
warn(message, meta?)
warn(message: string, meta?: Record<string, unknown>): void

Logs a warning message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
error(message, meta?)
error(message: string, meta?: Record<string, unknown>): void

Logs an error message.

Parameters:

  • message: The message to log
  • meta (optional): Additional metadata to include with the log
withContext(context)
withContext(context: Record<string, unknown>): Logger

Creates a new logger with the provided context merged with the existing logger's context.

Parameters:

  • context: Context data to include with all logs from the new logger
  • Returns: A new Logger instance with the combined context
setLevel(level)
setLevel(level: LogLevel): void

Sets the minimum log level.

Parameters:

  • level: The new minimum log level ('debug', 'info', 'warn', or 'error')
getLevel()
getLevel(): LogLevel

Gets the current log level.

Returns: The current log level

flush()
flush(): void

Flushes any buffered log entries. Mainly useful with asynchronous logging.

close()
close(): void

Closes the logger, releasing any resources and flushing any buffered logs.

Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Build the package
npm run build

# Generate API documentation
npm run docs

License

MIT