@hiennc24/common
v2.2.12
Published
Production-ready TypeScript utilities for Node.js: multi-tenant database management, NoSQL injection protection, error handling, validation, caching, and more. Battle-tested with 188/188 tests.
Maintainers
Readme
Stop Writing Boilerplate. Start Shipping Features.
Production-ready TypeScript utilities that eliminate 1000+ lines of repetitive code from your Node.js projects.
🚀 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/commonNo 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
🔒 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: '[email protected]',
$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: '[email protected]' } ✅
// 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-inFeatures:
- 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: '[email protected]',
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: '[email protected]', role: 'admin' }
// Exclude sensitive fields
const safeUser = FieldProjector.project(user, '-password -createdAt');
// { id: '123', email: '[email protected]', 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: '[email protected]' });
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: '[email protected]', 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
anyabuse - ✅ 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 detailsServerError(500) - Internal server errorsNotFoundError(404) - Resource not foundValidationError(400) - Input validation failedUnauthorizedError(401) - Authentication requiredPermissionDeniedError(403) - Insufficient permissionsBadRequestError(400) - Invalid request formatConflictError(409) - Resource conflictAlreadyExistsError(422) - Resource already existsTooManyRequestsError(429) - Rate limit exceededServiceUnavailableError(503) - Service unavailable
Error Utilities
ErrorUtils.createErrorResponse()- Standardized error responsesErrorUtils.isOperationalError()- Distinguish operational vs programming errorsErrorFactory.createValidationError()- Validation errors with field contextErrorFactory.createAuthError()- Authentication errorsErrorFactory.createNotFoundError()- Resource not found errors
Security
sanitizeMongoFilter(filter)- Remove dangerous MongoDB operatorsCrypto.encrypt(data, key)- AES encryptionCrypto.decrypt(data, key)- AES decryption
Database
ConnectMultipleDB- Multi-tenant database connection managergetConnection(domain)- Get/create connection for tenantcloseConnection(domain)- Close specific connectioncloseAllConnections()- Cleanup all connections
Validation
ValidationService.validateEntity()- Validate entity objectsValidationService.validateFilter()- Validate filter objectsValidationService.validateCondition()- Validate query conditionsValidationService.validatePipeline()- Validate aggregation pipelines
Field Projection
FieldProjector.project(entity, projection)- Apply MongoDB-style field filtering
Caching
CacheManager<T>- Redis caching with multi-tenancyset(key, value, options)- Cache dataget<T>(key)- Retrieve cached datadelete(key)- Remove from cacheclear()- Clear all cache
Helpers
isChanged(source, dest)- Deep equality checkfindChangedValue(source, dest)- Find changed propertieswaitByPromise(ms)- Async sleepisUndefinedOrNull(value)- Type guard for null/undefinedisStringAndNotEmpty(value)- Type guard for non-empty stringsgenerateTimestamp()- 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!
- Fork the repository: https://github.com/hiennc24/common
- Create your feature branch:
git checkout -b feature/amazing-feature - Run tests:
npm test(all 188 must pass) - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - 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