npm.io
2.3.3 • Published 5d ago

@hiennc24/common

Licence
ISC
Version
2.3.3
Deps
7
Size
341 kB
Vulns
0
Weekly
281

Stop Writing Boilerplate. Start Shipping Features.

Production-ready TypeScript utilities that eliminate 1000+ lines of repetitive code from your Node.js projects.

npm version tests TypeScript license

Quick Start

import { NotFoundError, sanitizeMongoFilter, CacheManager } from '@hiennc24/common';

// Production-ready error handling in one line
throw new NotFoundError('User not found', 'USER_NOT_FOUND', { userId: '123' });

// NoSQL injection protection built-in
const safeFilter = sanitizeMongoFilter(userInput); // Automatically strips $where, $function, etc.

// Multi-tenant caching without the headache
const cache = new CacheManager(redisClient, logger, 'user-service');

Why @hiennc24/common?

Because every Node.js project needs the same 80% of utilities, but nobody wants to write them twice.

  • Zero compromises on security - Built-in NoSQL injection prevention with circular reference protection
  • Battle-tested reliability - 188/188 tests passing, production-ready since v1.0
  • Type-safe by design - Full TypeScript definitions with intelligent autocomplete
  • Multi-tenant ready - Database connections and caching with tenant isolation out of the box
  • Actually maintained - Regular security audits, zero known vulnerabilities
  • Tree-shakeable - Import only what you need, ESM + CommonJS support

Installation

npm install @hiennc24/common

No peer dependencies. Works with Express, NestJS, Fastify, or any Node.js framework.


Core Features

Production-Ready Error Handling

Stop writing custom error classes. Get 10 HTTP-aware errors with proper TypeScript types and prototype chains.

Before @hiennc24/common:

// 30+ lines of boilerplate per error class
class NotFoundError extends Error {
  public code: number;
  public details?: any;
  constructor(message: string, details?: any) {
    super(message);
    this.name = 'NotFoundError';
    this.code = 404;
    this.details = details;
    Object.setPrototypeOf(this, NotFoundError.prototype);
  }
}
// Repeat for ValidationError, UnauthorizedError, ServerError...

After @hiennc24/common:

import {
  NotFoundError,
  ValidationError,
  UnauthorizedError,
  ErrorFactory
} from '@hiennc24/common';

// Use directly
throw new NotFoundError('User not found', 'USER_NOT_FOUND', { userId: '123' });

// Or use the factory for common patterns
throw ErrorFactory.createNotFoundError('User', userId, { requestId });
throw ErrorFactory.createValidationError('Invalid email', ['email'], { value });

Includes: ServerError, NotFoundError, ValidationError, UnauthorizedError, PermissionDeniedError, BadRequestError, ConflictError, AlreadyExistsError, TooManyRequestsError, ServiceUnavailableError


Production-Ready Money Arithmetic

Stop losing pennies. Integer-only math with allocate algorithms that guarantee sum invariants.

The problem: 0.1 + 0.2 === 0.30000000000000004 silently drains millions across millions of transactions.

The solution: Store and compute everything in minor units (cents/xu), per Stripe/PayPal pattern.

import { money } from '@hiennc24/common';

// Parse user input at the boundary (rejects float / scientific / separators)
const a = money.parseMoney('0.10', 2); // 10 (cents)
const b = money.parseMoney('0.20', 2); // 20 (cents)
money.add(a, b);                       // 30 — exact, always

// Split a bill — zero orphaned pennies
money.allocate(100000, 3);             // [33334, 33333, 33333] — sum === 100000 GUARANTEED

// Weighted distribution (e.g. cost allocation)
money.allocateByRatio(100, [50, 30, 20]); // [50, 30, 20]

// Tax / interest with zero float intermediate
money.mulByBasisPoints(1050, 1000);    // 105 (10% of $10.50)

// Division returns quotient + remainder — never silently rounds
money.div(1000, 3);                    // { quotient: 333, remainder: 1 }

// Format only at the UI boundary
money.formatMoney(1050, 2);            // '10.50'

Includes: parseMoney, formatMoney, add, sub, mul, mulByBasisPoints, div, allocate, allocateByRatio

Throws ValidationError on: float input, NaN/Infinity, scientific notation, precision loss, overflow, zero divisor, invalid ratios.


Built-in Security Against NoSQL Injection

Your MongoDB queries are only as secure as your input sanitization.

import { sanitizeMongoFilter } from '@hiennc24/common';

// Dangerous user input
const maliciousInput = {
  email: 'user@example.com',
  $where: 'this.password.length > 0', // 🚨 Injection attempt - blocked
  $function: { body: 'return true' }   // 🚨 Code execution - blocked
};

// Automatically strips dangerous operators
const safeFilter = sanitizeMongoFilter(maliciousInput);
// Result: { email: 'user@example.com' } ✅

// Handles circular references (prevents DoS)
const circular: any = { a: 1 };
circular.self = circular;
sanitizeMongoFilter(circular); // Won't crash your app 

Protects against: $where, $function, $expr, $accumulator, $mapReduce, group, and other code execution operators. Safe query operators like $ne, $regex, $in are allowed.


Multi-Tenant Database Management

Managing connections for 100+ tenants? ConnectMultipleDB handles caching, retry logic, and automatic cleanup.

import { ConnectMultipleDB } from '@hiennc24/common';

const dbManager = new ConnectMultipleDB(
  logger,
  redisCache,
  { getConnectionString: async (domain) => fetchFromVault(domain) },
  { maxPoolSize: 10, maxConnecting: 5 }
);

// Automatically caches connections per tenant
const tenantA_DB = await dbManager.getConnection('tenant-a.com');
const tenantB_DB = await dbManager.getConnection('tenant-b.com');

// Auto-closes idle connections after 2 hours (configurable)
// Retry logic with exponential backoff built-in

Features:

  • Connection pooling per tenant
  • Redis-backed caching (1-hour TTL)
  • Automatic idle connection cleanup
  • Configurable retry attempts (default: 5)
  • Domain validation and encryption support

Type-Safe Validation Service

Stop repeating the same null checks and type guards.

import { ValidationService } from '@hiennc24/common';

// Before: Manual validation everywhere
if (!entity || typeof entity !== 'object') {
  throw new Error('Invalid entity');
}
if (!filter || typeof filter !== 'object') {
  throw new Error('Invalid filter');
}

// After: One-liner validation with context
ValidationService.validateEntity(entity, 'createUser');
ValidationService.validateFilter(filter, 'findUsers');
ValidationService.validateCondition(condition, 'updateUser');
ValidationService.validatePipeline(pipeline, 'aggregateData');

// Throws ValidationError with method context for better debugging

MongoDB-Style Field Projection

Filter entity fields like MongoDB projections, but for plain JavaScript objects.

import { FieldProjector } from '@hiennc24/common';

const user = {
  id: '123',
  email: 'user@example.com',
  password: 'hashed_password',
  role: 'admin',
  createdAt: new Date()
};

// Include only specific fields (MongoDB-style)
const publicUser = FieldProjector.project(user, 'id email role');
// { id: '123', email: 'user@example.com', role: 'admin' }

// Exclude sensitive fields
const safeUser = FieldProjector.project(user, '-password -createdAt');
// { id: '123', email: 'user@example.com', role: 'admin' }

// Object notation also supported
const projected = FieldProjector.project(user, { email: 1, role: 1 });

Perfect for: API responses, logging sanitization, DTOs, GraphQL resolvers


Smart Caching with Multi-Tenancy

Redis caching that understands your multi-tenant architecture.

import { CacheManager } from '@hiennc24/common';

const cache = new CacheManager<User>(
  redisClient,
  logger,
  'user-service',
  'tenant-a.com' // Automatic tenant isolation
);

// Cache with automatic key generation
await cache.set('user:123', userData, { ttl: 3600 });

// Get with type safety
const user = await cache.get<User>('user:123');

// Tenant-specific keys automatically
// Actual Redis key: "tenant-a.com:user-service:user:123"

Features:

  • Automatic tenant isolation
  • Release version support (cache invalidation on deploy)
  • Reference-based caching for complex objects
  • Type-safe generics
  • Development mode bypass

AES Encryption Utilities

Simple, secure encryption for configuration and secrets.

import { Crypto } from '@hiennc24/common';

const encrypted = Crypto.encrypt('sensitive-data', 'your-secret-key');
const decrypted = Crypto.decrypt(encrypted, 'your-secret-key');

// Perfect for: API keys in config, connection strings, tokens

Structured Logging with Winston

Pre-configured Winston logger with context support.

import { logger } from '@hiennc24/common';

logger.info('User created', { userId: '123', email: 'user@example.com' });
logger.error('Database error', { error: err.message, stack: err.stack });
logger.warn('Rate limit approaching', { requests: 95, limit: 100 });

Essential Helpers

Utilities you copy-paste between projects.

import {
  isChanged,
  findChangedValue,
  waitByPromise,
  isUndefinedOrNull,
  isStringAndNotEmpty
} from '@hiennc24/common';

// Detect changes between objects
const hasChanges = isChanged(originalUser, updatedUser);

// Find exactly what changed
const delta = findChangedValue(originalUser, updatedUser);
// { email: 'new@example.com', role: 'admin' }

// Async sleep
await waitByPromise(1000); // Wait 1 second

// Type guards
if (isStringAndNotEmpty(value)) {
  // TypeScript knows value is string
}

Battle-Tested Quality

  • 188/188 tests passing - Comprehensive test coverage for every feature
  • 100% TypeScript - Full type definitions with zero any abuse
  • Zero security vulnerabilities - Regular audits with npm audit
  • Production-ready - Used in multi-tenant SaaS platforms since v1.0
  • Actively maintained - Security patches within 48 hours

Full API Reference

Error Classes
  • AppError - Base error class with enhanced details
  • ServerError (500) - Internal server errors
  • NotFoundError (404) - Resource not found
  • ValidationError (400) - Input validation failed
  • UnauthorizedError (401) - Authentication required
  • PermissionDeniedError (403) - Insufficient permissions
  • BadRequestError (400) - Invalid request format
  • ConflictError (409) - Resource conflict
  • AlreadyExistsError (422) - Resource already exists
  • TooManyRequestsError (429) - Rate limit exceeded
  • ServiceUnavailableError (503) - Service unavailable
Error Utilities
  • ErrorUtils.createErrorResponse() - Standardized error responses
  • ErrorUtils.isOperationalError() - Distinguish operational vs programming errors
  • ErrorFactory.createValidationError() - Validation errors with field context
  • ErrorFactory.createAuthError() - Authentication errors
  • ErrorFactory.createNotFoundError() - Resource not found errors
Security
  • sanitizeMongoFilter(filter) - Remove dangerous MongoDB operators
  • Crypto.encrypt(data, key) - AES encryption
  • Crypto.decrypt(data, key) - AES decryption
Database
  • ConnectMultipleDB - Multi-tenant database connection manager
    • getConnection(domain) - Get/create connection for tenant
    • closeConnection(domain) - Close specific connection
    • closeAllConnections() - Cleanup all connections
Validation
  • ValidationService.validateEntity() - Validate entity objects
  • ValidationService.validateFilter() - Validate filter objects
  • ValidationService.validateCondition() - Validate query conditions
  • ValidationService.validatePipeline() - Validate aggregation pipelines
Field Projection
  • FieldProjector.project(entity, projection) - Apply MongoDB-style field filtering
Caching
  • CacheManager<T> - Redis caching with multi-tenancy
    • set(key, value, options) - Cache data
    • get<T>(key) - Retrieve cached data
    • delete(key) - Remove from cache
    • clear() - Clear all cache
Helpers
  • isChanged(source, dest) - Deep equality check
  • findChangedValue(source, dest) - Find changed properties
  • waitByPromise(ms) - Async sleep
  • isUndefinedOrNull(value) - Type guard for null/undefined
  • isStringAndNotEmpty(value) - Type guard for non-empty strings
  • generateTimestamp() - Get current timestamp
Logging
  • logger.info(), logger.error(), logger.warn(), logger.debug() - Winston-based logging

Contributing

Found a bug? Have a feature request? Contributions welcome!

  1. Fork the repository: https://github.com/hiennc24/common
  2. Create your feature branch: git checkout -b feature/amazing-feature
  3. Run tests: npm test (all 188 must pass)
  4. Commit changes: git commit -m 'Add amazing feature'
  5. Push to branch: git push origin feature/amazing-feature
  6. Open a Pull Request

Before submitting:

  • Add tests for new features
  • Ensure TypeScript types are correct
  • Update documentation if needed

Issues & Support

  • Bug Reports: GitHub Issues
  • Security Issues: Email author directly (see package.json)
  • Questions: Open a discussion on GitHub

License

ISC License - Copyright (c) Hien Nguyen

See LICENSE for details.


Support This Project

If @hiennc24/common saves you from writing boilerplate, give it a star on GitHub!

Star on GitHub

Every star helps other developers discover this package and motivates continued maintenance.


Package Stats

  • Bundle Size: Tree-shakeable ESM + CommonJS
  • Dependencies: 6 production dependencies (axios, cron, crypto-js, express, lodash, winston)
  • Node Version: 14+ (TypeScript 4.9+)
  • Framework Agnostic: Works with Express, NestJS, Fastify, Koa, etc.

Stop reinventing the wheel. Start with @hiennc24/common.

npm install @hiennc24/common

Keywords