rezilia
v0.1.1
Published
π‘οΈ The ultimate backend resilience toolkit β Smart Cache (stampede prevention), Smart Logger (async context), Smart Retry (circuit breaker). Works with npm, yarn, pnpm, and bun.
Maintainers
Readme
rezilia π‘οΈ
The ultimate backend resilience toolkit for Node.js β Cache Stampede Prevention, Async-Context Logger, and Circuit Breaker Retry in one package.
Why rezilia?
Every production Node.js backend hits the same three walls:
| Problem | What happens without it | rezilia's solution |
|---------|------------------------|-------------------|
| Cache Stampede | 1,000 requests hit DB simultaneously when cache expires | Singleflight β only 1 fetcher runs, all callers share the result |
| Lost Log Context | Request ID disappears across async/await boundaries | AsyncLocalStorage auto-propagation β no manual passing |
| Cascading Failures | One slow API brings down your whole backend | Circuit Breaker + Exponential Backoff with jitter |
Zero dependencies. Full TypeScript. Works with npm, yarn, pnpm, and bun.
Install
npm install rezilia
yarn add rezilia
pnpm add rezilia
bun add reziliaRedis support (optional β for distributed caching):
npm install ioredis
Quick Start
import { SmartCache, SmartLogger, retry } from 'rezilia';
// βββ Cache: 1000 concurrent requests β only 1 DB call ββββββββββββββββββ
const cache = new SmartCache({ defaultTtl: 60_000 });
const user = await cache.get('user:42', () => db.findUser(42));
// βββ Logger: requestId flows through all async calls automatically βββββββ
const logger = new SmartLogger({ serviceName: 'api' });
logger.runWithContext({ requestId: req.id }, async () => {
logger.info('Processing payment'); // β includes requestId automatically
await processPayment();
logger.info('Payment done'); // β requestId still here β
});
// βββ Retry: exponential backoff + circuit breaker βββββββββββββββββββββββ
const data = await retry(() => fetch('/api/data').then(r => r.json()), {
attempts: 3,
timeout: 5_000,
circuitBreaker: { threshold: 5, resetAfter: 30_000 },
});Modules
π¦ SmartCache β Singleflight + SWR + Two-Layer Cache
The #1 missing primitive in the JS ecosystem. When your cache expires under high load, only 1 fetcher runs β all concurrent callers wait and share the single result. No thundering herd. No DB meltdown.
import { SmartCache } from 'rezilia/cache';
const cache = new SmartCache({
defaultTtl: 60_000, // 1 minute
jitterFactor: 0.1, // Β±10% TTL randomness (prevents mass simultaneous expiry)
staleWhileRevalidate: true, // serve stale data instantly while refreshing in background
staleTtl: 5_000, // serve stale for up to 5s
});
// Singleflight in action β 100 concurrent callers, fetcher runs exactly ONCE
const results = await Promise.all(
Array.from({ length: 100 }, () => cache.get('hot-key', expensiveFetcher))
);
// fetcher was called: 1 time β
// With custom TTL per key
const session = await cache.get('session:abc', fetchSession, { ttl: 3_600_000 });
// Manual set / invalidate
await cache.set('config', appConfig, { ttl: 86_400_000 });
await cache.invalidate('product:123');
await cache.clear();Redis Store
import Redis from 'ioredis';
import { SmartCache, RedisStore } from 'rezilia/cache';
const cache = new SmartCache({
store: new RedisStore(new Redis({ host: 'localhost', port: 6379 })),
defaultTtl: 60_000,
});π SmartLogger β Async Context Propagation
Structured JSON logging with automatic requestId propagation through the entire async call chain. No more passing logger or requestId through every function parameter.
import { SmartLogger } from 'rezilia/logger';
const logger = new SmartLogger({
serviceName: 'payment-api',
level: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
prettyPrint: false, // true in dev, false in prod
redactFields: ['ssn'], // extend the built-in redacted fields
});
logger.info('Server started', { port: 3000 });
logger.warn('High memory usage', { heapUsed: '512MB' });
logger.error('DB connection failed', new Error('ECONNREFUSED'));
// Auto-redaction β sensitive fields are NEVER logged
logger.info('Login', { username: '[email protected]', password: 'secret123' });
// β {"data":{"username":"[email protected]","password":"[REDACTED]"}}
// Scoped child logger
const reqLogger = logger.child({ requestId: 'abc-123', userId: '42' });
reqLogger.info('Payment started'); // always includes requestId + userIdBuilt-in redacted fields: password, token, accessToken, refreshToken, secret, authorization, cookie, apiKey, privateKey, cvv, cardNumber
Express / Fastify Middleware
import express from 'express';
import { SmartLogger, requestLogger } from 'rezilia/logger';
const app = express();
const logger = new SmartLogger({ serviceName: 'api' });
app.use(requestLogger(logger));
app.get('/users/:id', async (req, res) => {
logger.info('Fetching user'); // requestId here automatically β no passing needed!
res.json(await db.getUser(req.params.id));
});Sample output:
{
"level": "info",
"message": "Fetching user",
"timestamp": "2026-09-08T10:00:00.000Z",
"context": {
"service": "api",
"requestId": "a3f8e2b1-...",
"method": "GET",
"path": "/users/42"
}
}π SmartRetry β Circuit Breaker + Exponential Backoff
Production-grade retry logic with per-attempt timeouts, jitter to avoid thundering herd, and an integrated circuit breaker.
import { SmartRetry, retry, CircuitBreaker } from 'rezilia/retry';
// One-liner for simple use cases
const data = await retry(() => api.getData(), { attempts: 3, baseDelay: 500 });
// Full control
const retrier = new SmartRetry({
attempts: 5,
baseDelay: 500, // ms β doubles each attempt: 500 β 1000 β 2000 β 4000
maxDelay: 30_000, // never wait more than 30s
jitter: 0.2, // Β±20% randomness β prevents synchronized retries
timeout: 10_000, // fail each attempt after 10s
shouldRetry: (err, attempt) => {
if (err instanceof HttpError && err.status < 500) return false;
return true;
},
onRetry: (err, attempt, delay) => {
logger.warn(`Attempt ${attempt} failed, retrying in ${delay}ms`);
},
circuitBreaker: {
threshold: 5, // open after 5 consecutive failures
resetAfter: 30_000, // probe again after 30s
halfOpenSuccesses: 2, // need 2 successes to fully close
},
});
const result = await retrier.execute(() => paymentGateway.charge(order));
console.log(retrier.circuitState); // 'CLOSED' | 'OPEN' | 'HALF_OPEN'
retrier.resetCircuit(); // manual reset (useful in tests)Circuit Breaker States:
[CLOSED] β5 failuresβ [OPEN] β30s timeoutβ [HALF_OPEN] β2 successesβ [CLOSED]
β |
βββββββfailureββββββββββββTree-shakeable Imports
import { SmartCache } from 'rezilia/cache'; // cache only
import { SmartLogger } from 'rezilia/logger'; // logger only
import { retry } from 'rezilia/retry'; // retry only
import { SmartCache, SmartLogger, retry } from 'rezilia'; // everythingWorks with ESM and CommonJS:
import { SmartCache } from 'rezilia/cache'; // ESM
const { SmartCache } = require('rezilia/cache'); // CommonJSWhy not X?
| Feature | rezilia | node-cache | winston | axios-retry | |---------|---------|-----------|---------|-------------| | Singleflight / stampede prevention | β | β | β | β | | Stale-While-Revalidate | β | β | β | β | | TTL jitter | β | β | β | β | | Async context propagation | β | β | β | β | | Auto secret redaction | β | β | β | β | | Circuit breaker | β | β | β | β οΈ partial | | Per-attempt timeout | β | β | β | β | | Zero runtime dependencies | β | β | β | β | | Works with Bun | β | β | β | β |
Requirements
| Runtime | Version | |---------|---------| | Node.js | β₯ 18.0.0 | | Bun | β₯ 1.0.0 | | TypeScript | β₯ 5.0 (optional) |
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
git clone https://github.com/Work-AbhishekMajumdar/rezilia.git
cd rezilia
npm install
npm test
npm run buildLicense
MIT Β© 2026 Abhishek Majumdar
