npm package: @smmorshed/server-utils
Description: Professional utility package for Express.js-based Node servers
Latest version: 0.0.1

# Server Utils

[![CI](https://github.com/morshed33/@smmorshed/server-utils/actions/workflows/ci.yml/badge.svg)](https://github.com/morshed33/@smmorshed/server-utils/actions/workflows/ci.yml)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@smmorshed/server-utils)](https://bundlephobia.com/package/@smmorshed/server-utils)
[![npm version](https://img.shields.io/npm/v/@smmorshed/server-utils.svg)](https://www.npmjs.com/package/@smmorshed/server-utils)

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

```bash
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
constructor(options?: LoggerOptions)
```

**Parameters:**

- `options` (optional): Configuration options for the logger

#### LoggerOptions Interface

```typescript
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:

```typescript
// 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?)

```typescript
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?)

```typescript
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?)

```typescript
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?)

```typescript
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)

```typescript
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)

```typescript
setLevel(level: LogLevel): void
```

Sets the minimum log level.

**Parameters:**

- `level`: The new minimum log level ('debug', 'info', 'warn', or 'error')

##### getLevel()

```typescript
getLevel(): LogLevel
```

Gets the current log level.

**Returns**: The current log level

##### flush()

```typescript
flush(): void
```

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

##### close()

```typescript
close(): void
```

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

## Development

```bash
# 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