@thekosmix/node-decorators
v1.2.1
Published
A production-grade Spring-Boot-inspired TypeScript decorator suite for caching (Memory & Redis), logging, resiliency, metrics, and API exposure.
Maintainers
Readme
@thekosmix/node-decorators
A production-grade, Spring-Boot-inspired TypeScript decorator suite for Node.js. Carve out boilerplate like caching (In-Memory & Redis), logging, resiliency, metrics, validation, distributed locking, circuit breaking, idempotency, transactions, authorization, and audit trails with clean annotations.
Table of Contents
- Overview
- What's New in v1.1.0
- How to Use in an Existing Node.js Project
- How to Test & Build
- Features & API Reference
- 1. Caching Suite (@Cache, @CacheEvict, @CachePut)
- 2. Logging Suite (@Log)
- 3. Resiliency Suite (@Retry, @Timeout, @RateLimit)
- 4. Distributed Systems & Concurrency (@Lock, @CircuitBreaker)
- 5. API & Data Integrity (@Idempotent, @Transactional)
- 6. Security & Governance (@Authorize, @Roles, @Audit, @FeatureFlag)
- 7. Traffic Management & Performance (@SingleFlight, @Debounce, @Throttle)
- 8. Validation, Diagnostics & Fallback (@Validate, @Timed, @Catch, @Fallback, @Length, @Async, @Rest)
- License
Overview
In Java's Spring Boot ecosystem, developers use annotations to separate cross-cutting concerns (caching, logging, retries, rate limits, locks, transactions) from business logic. node-decorators brings that exact ergonomic power to TypeScript and Node.js.
Instead of writing repetitive boilerplate inside your services:
// Without decorators: repetitive boilerplate mixed into business logic
async function getUsernameById(userId: string) {
const cached = await redis.get(`users:${userId}`);
if (cached) return JSON.parse(cached);
const start = Date.now();
try {
const user = await UserModel.findById(userId);
await redis.set(`users:${userId}`, JSON.stringify(user.name), 'EX', 3600);
logger.info(`getUsernameById took ${Date.now() - start}ms`);
return user.name;
} catch (err) {
logger.error('Failed to get user', err);
throw err;
}
}Decorate your methods cleanly:
import { Cache, Log, Retry, Lock } from '@thekosmix/node-decorators';
class UserService {
@Cache({ store: 'redis', bucket: 'users', ttl: 3600 })
@Log()
@Retry({ maxAttempts: 3 })
@Lock({ key: (userId) => `user:${userId}` })
async getUsernameById(userId: string) {
const user = await UserModel.findById(userId);
return user.name;
}
}What's New in v1.1.0
Version 1.1.0 expands the suite with 10 enterprise utilities for distributed systems, security, transactions, and performance:
- 🔒 Distributed Concurrency & Failsafe: Added
@Lock(Redis atomicSET NX PXdistributed mutex & in-memory fallback) and@CircuitBreaker(automatedCLOSED→OPEN→HALF_OPENoutage protection). - 🔁 API & Data Integrity: Added
@Idempotent(deduplication of retried requests/webhooks) and@Transactional(declarative database transactions with Node.jsAsyncLocalStoragecontext propagation). - 🛡️ Security & Compliance: Added
@Authorize/@Roles(declarative RBAC and permission checking),@Audit(automatic compliance audit trails), and@FeatureFlag(dynamic runtime feature toggles). - ⚡ Traffic & Performance: Added
@SingleFlight(eliminates the thundering herd by coalescing concurrent in-flight calls),@Debounce, and@Throttle. - ✅ Test Coverage & Reliability: 20 complete test suites with 70 passing automated tests covering all decorators and edge cases.
How to Use in an Existing Node.js Project
Step 1: Install the Package
npm install @thekosmix/node-decorators reflect-metadataIf you plan to use Redis caching or Redis distributed locking, install ioredis:
npm install ioredisStep 2: Configure TypeScript (tsconfig.json)
Decorators require experimental decorator support and decorator metadata. Ensure your tsconfig.json contains:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false
}
}Step 3: Initialize reflect-metadata at Application Entry
Import reflect-metadata once at the very top of your application entry file (index.ts, server.ts, app.ts):
// src/index.ts or server.ts
import 'reflect-metadata';Step 4: Use Decorators in Your Services (ESM & CommonJS)
In ES Modules (import):
import { Cache, Log, Retry, Lock, CircuitBreaker } from '@thekosmix/node-decorators';
export class OrderService {
@Lock({ key: (orderId) => `order:${orderId}` })
@CircuitBreaker({ failureThreshold: 3, resetTimeoutMs: 5000 })
@Retry({ maxAttempts: 3, delayMs: 200 })
async processOrder(orderId: string, amount: number) {
return paymentGateway.charge(orderId, amount);
}
}In CommonJS (require):
const { Cache, Log, Retry } = require('@thekosmix/node-decorators');Step 5: (Optional) Connect Redis or File Logging
import Redis from 'ioredis';
import { CacheManager, RedisCacheStore, LoggerManager } from '@thekosmix/node-decorators';
// Connect Redis (enables both Redis Caching and Redis Distributed Locking)
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
CacheManager.registerStore('redis', new RedisCacheStore(redis));
// Enable disk file logging
LoggerManager.enableFileLogging('./logs/app.log', 'json');Testing Locally in Another Project via npm link
- In
node-decorators:npm run build npm link - In your consumer project:
npm link @thekosmix/node-decorators
How to Test & Build
Running the Test Suite
# Run all 20 test suites (70 unit & integration tests)
npm test
# Run tests in interactive watch mode
npm run test:watchRunning Specific Test Specs
npx vitest tests/lock.spec.ts
npx vitest tests/circuit-breaker.spec.ts
npx vitest tests/idempotent.spec.ts
npx vitest tests/transactional.spec.ts
npx vitest tests/validate.spec.ts
npx vitest tests/authorize.spec.ts
npx vitest tests/audit.spec.ts
npx vitest tests/feature-flag.spec.ts
npx vitest tests/single-flight.spec.ts
npx vitest tests/debounce.spec.tsRunning the Sample Demo Runner
npx ts-node test.tsBuilding for Production (ESM + CommonJS + Types)
npm run buildGenerates:
dist/index.js(CommonJS bundle)dist/index.mjs(ES Module bundle)dist/index.d.ts&dist/index.d.mts(Complete TypeScript typings)
Verifying the npm Package
npm pack --dry-runFeatures & API Reference
1. Caching Suite
Supports multiple named stores, asynchronous and synchronous execution, automatic bucket partitioning, and custom key generation.
In-Memory Caching (Default)
import { Cache } from '@thekosmix/node-decorators';
class ProductService {
@Cache('products')
getProduct(id: string) {
return db.products.findById(id);
}
}Redis Caching
import Redis from 'ioredis';
import { Cache, CacheManager, RedisCacheStore } from '@thekosmix/node-decorators';
CacheManager.registerStore('redis', new RedisCacheStore(new Redis()));
class OrderService {
@Cache({ store: 'redis', bucket: 'orders', ttl: 600 })
async getOrder(orderId: string) {
return db.orders.findById(orderId);
}
}Cache Eviction & Updates
import { Cache, CacheEvict, CachePut } from '@thekosmix/node-decorators';
class UserService {
@Cache('users')
getUser(id: string) { ... }
@CachePut({ bucket: 'users', keyGenerator: (user) => user.id })
updateUser(user: { id: string; name: string }) { ... }
@CacheEvict('users')
deleteUser(id: string) { ... }
@CacheEvict({ bucket: 'users', allEntries: true })
flushUserCache() {}
}2. Logging Suite
The @Log decorator captures invocation arguments, execution duration ([12.45ms]), return values, and exceptions with stack traces.
import { Log, LoggerManager } from '@thekosmix/node-decorators';
// Optional: Enable disk logs or external logger
LoggerManager.enableFileLogging('./logs/app.log', 'json');
class AuthService {
@Log({ maskParams: ['password', 'token'] })
login(payload: { username: string; password: string }) { ... }
}3. Resiliency Suite
@Retry
Retries failed operations with configurable attempts, delays, and exponential backoff:
import { Retry } from '@thekosmix/node-decorators';
class ExternalApi {
@Retry({ maxAttempts: 3, delayMs: 200, backoffMultiplier: 2 })
async callWebhook(payload: any) { ... }
}@Timeout
Aborts and rejects with TimeoutError if a method takes longer than the threshold:
import { Timeout } from '@thekosmix/node-decorators';
class SearchService {
@Timeout(3000)
async searchIndex(query: string) { ... }
}@RateLimit
Enforces sliding window call limits globally or per user:
import { RateLimit } from '@thekosmix/node-decorators';
class NotificationService {
@RateLimit({ maxCalls: 5, windowMs: 60000, keyGenerator: (userId) => userId })
sendPush(userId: string, message: string) { ... }
}4. Distributed Systems & Concurrency
@Lock
Distributed mutex across processes (via Redis atomic SET NX PX and Lua unlock) or in-memory Promise queue:
import { Lock } from '@thekosmix/node-decorators';
class PaymentService {
@Lock({ key: (userId) => `payment:${userId}`, ttlMs: 5000, wait: true })
async processPayment(userId: string, amount: number) {
// Guaranteed single-execution across distributed workers
}
}@CircuitBreaker
Fail-fast protection against cascading downstream outages:
import { CircuitBreaker } from '@thekosmix/node-decorators';
class RecommendationService {
@CircuitBreaker({
failureThreshold: 5,
resetTimeoutMs: 10000,
fallback: () => ({ cached: true, items: [] }),
})
async getRemoteRecommendations(userId: string) {
return remoteClient.fetch(userId);
}
}5. API & Data Integrity
@Idempotent
Prevents duplicate processing of retried network calls or duplicate webhooks:
import { Idempotent } from '@thekosmix/node-decorators';
class CheckoutService {
@Idempotent({ keyGenerator: (orderId) => orderId, ttlSeconds: 86400 })
async chargeOrder(orderId: string, amount: number) {
// If called twice with the same orderId, returns the cached result without charging again
}
}@Transactional
Automated database transaction management with atomic commit and rollback on error, propagated via Node.js AsyncLocalStorage:
import { Transactional, TransactionManager } from '@thekosmix/node-decorators';
// Setup your ORM/query runner provider once
TransactionManager.setProvider({
beginTransaction: () => db.startTransaction(),
commit: (tx) => tx.commit(),
rollback: (tx) => tx.rollback(),
});
class AccountService {
@Transactional()
async transfer(fromId: string, toId: string, amount: number) {
const tx = TransactionManager.getTransaction();
await db.debit(fromId, amount, tx);
await db.credit(toId, amount, tx);
}
}6. Security & Governance
@Authorize & @Roles
Declarative role-based access control and permission checking:
import { Authorize, Roles, SecurityContext } from '@thekosmix/node-decorators';
class DocumentService {
@Roles('ADMIN', 'EDITOR')
editDocument(docId: string) { ... }
@Authorize({ permissions: ['docs:publish'] })
publishDocument(docId: string) { ... }
}@Audit
Automatic compliance audit logging:
import { Audit, AuditManager } from '@thekosmix/node-decorators';
AuditManager.setHandler(async (record) => {
await auditDb.insert(record);
});
class AdminService {
@Audit({ action: 'PROMOTE_USER', actor: (adminId) => adminId, target: (_, userId) => userId })
promoteUser(adminId: string, userId: string, newRole: string) { ... }
}@FeatureFlag
Dynamic feature toggling and fallback routing:
import { FeatureFlag, FeatureFlagManager } from '@thekosmix/node-decorators';
FeatureFlagManager.setFlag('v2-pricing', true);
class BillingService {
@FeatureFlag('v2-pricing', { fallback: () => 100 })
calculatePricing(plan: string) {
return 150;
}
}7. Traffic Management & Performance
@SingleFlight
Coalesces concurrent in-flight calls for identical keys into a single execution to prevent the "thundering herd" problem:
import { SingleFlight } from '@thekosmix/node-decorators';
class NewsService {
@SingleFlight((articleId) => articleId)
async getTrendingArticle(articleId: string) {
// 50 simultaneous callers will only execute this query once!
return db.queryArticle(articleId);
}
}@Debounce & @Throttle
import { Debounce, Throttle } from '@thekosmix/node-decorators';
class EditorService {
@Debounce(300)
autoSave(document: any) { ... }
@Throttle(1000)
syncCursor(position: any) { ... }
}8. Validation, Diagnostics & Fallback
@Validate(schema): Validates arguments before execution (supports Zod, Joi, or custom predicates).@Timed()/@MeasureTime(): High-precision execution duration tracking.@Catch(handler)&@Fallback(value): Intercept exceptions and return graceful fallbacks withouttry/catch.@Length(min, max): Validates property length constraints.@Async: Defer method execution to the next event loop tick and return a Promise.@Rest: Expose methods as REST endpoints withstartRestServer(3000).
