Production-ready HTTP client with advanced retry strategies, circuit breaker, DLQ, and telemetry
npm install elastic-reqA production-ready, enterprise-grade HTTP client built on Axios with advanced retry strategies, circuit breaker pattern, dead letter queue (DLQ), and comprehensive telemetry.
- ๐ Multiple Retry Strategies: Exponential, Linear, Fibonacci, and Jittered backoff
- โก Circuit Breaker Pattern: Prevent cascading failures and graceful recovery
- ๐ฌ Dead Letter Queue (DLQ): Track and manage failed requests with persistence support
- ๐ Built-in Telemetry: Real-time metrics, latency tracking, and success rates
- ๐ฏ Memory Optimized: Bounded memory usage for high-traffic applications
- ๐ง Fully Configurable: Granular control over every aspect
- ๐ช TypeScript First: Complete type safety and IntelliSense support
- ๐ Production Ready: Battle-tested patterns for thousands of requests
``bash`
npm install elastic-reqor
yarn add elastic-reqor
pnpm add elastic-req
`typescript
import ElasticReqClient, { RetryStrategy } from 'elastic-req';
// Basic usage with defaults
const client = new ElasticReqClient();
// Make requests
const response = await client.get('https://api.example.com/users');
// With custom configuration
const client = new ElasticReqClient({
baseURL: 'https://api.example.com',
maxRetries: 5,
strategy: RetryStrategy.EXPONENTIAL,
timeoutMs: 10000,
headers: {
'Authorization': 'Bearer token'
}
});
`
`typescript
const client = new ElasticReqClient({
// Base Configuration
baseURL: 'https://api.example.com',
headers: { 'Authorization': 'Bearer token' },
timeoutMs: 5000,
// Retry Configuration
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 30000,
strategy: RetryStrategy.EXPONENTIAL,
jitterFactor: 0.1,
retryableStatuses: [408, 429, 500, 502, 503, 504],
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED', 'ENETUNREACH'],
// Circuit Breaker Configuration
circuitBreaker: {
failureThreshold: 5,
resetTimeoutMs: 60000,
monitoringWindowMs: 10000,
},
// Dead Letter Queue Configuration
dlq: {
maxSize: 1000,
enableMetrics: true,
persistenceAdapter: customPersistenceAdapter,
retryScheduler: customRetryScheduler,
},
// Callback for DLQ entries
onDLQEntry: (entry) => {
console.error('Request failed permanently:', entry);
// Send alert, log to monitoring system, etc.
}
});
`
`typescript
// GET request
const users = await client.get('/users');
// POST request
const newUser = await client.post('/users', {
name: 'John Doe',
email: 'john@example.com'
});
// PUT request
const updated = await client.put('/users/123', { name: 'Jane Doe' });
// DELETE request
await client.delete('/users/123');
// PATCH request
await client.patch('/users/123', { status: 'active' });
`
`typescript
interface User {
id: number;
name: string;
email: string;
}
const response = await client.get
const users: User[] = response.data;
const newUser = await client.post
name: 'John',
email: 'john@example.com'
});
`
`typescript
// Get all failed requests
const failedRequests = client.dlq.getAll();
// Get specific entry
const entry = client.dlq.get('dlq_123_abc');
// Remove entry
client.dlq.remove('dlq_123_abc');
// Clear all entries
client.dlq.clear();
// Get DLQ metrics
const dlqMetrics = await client.dlq.getMetrics();
console.log(dlqMetrics);
// {
// totalFailed: 42,
// byStatus: { '500': 15, '503': 27 },
// byEndpoint: { '/api/users': 10, '/api/orders': 32 },
// oldestFailure: { ... }
// }
`
`typescript
import { PersistenceAdapter, DLQEntry } from 'elastic-req';
class RedisPersistenceAdapter implements PersistenceAdapter {
async save(entry: DLQEntry): Promise
await redis.set(dlq:${entry.id}, JSON.stringify(entry));
}
async load(limit?: number): Promise
const keys = await redis.keys('dlq:*');
const entries = await Promise.all(
keys.slice(0, limit).map(k => redis.get(k))
);
return entries.map(e => JSON.parse(e));
}
}
const client = new ElasticReqClient({
dlq: {
persistenceAdapter: new RedisPersistenceAdapter()
}
});
`
`typescript
import { RetryScheduler, DLQEntry } from 'elastic-req';
class CustomRetryScheduler implements RetryScheduler {
schedule(entry: DLQEntry): void {
const retryAt = new Date(entry.retryScheduledAt);
const delay = retryAt.getTime() - Date.now();
setTimeout(async () => {
try {
// Retry the request
await client.request({
url: entry.url,
method: entry.method,
data: entry.data,
});
console.log(โ
Retry successful for ${entry.id});โ Retry failed for ${entry.id}
} catch (err) {
console.error();
}
}, delay);
}
}
const client = new ElasticReqClient({
dlq: {
retryScheduler: new CustomRetryScheduler()
}
});
`
`typescript
client.circuitBreaker.on('open', () => {
console.error('๐ด Circuit breaker opened - requests will be rejected');
// Send alert to monitoring system
});
client.circuitBreaker.on('halfOpen', () => {
console.warn('๐ก Circuit breaker testing recovery');
});
client.circuitBreaker.on('close', () => {
console.info('๐ข Circuit breaker closed - back to normal');
});
// Manual circuit breaker check
if (client.circuitBreaker.isOpen()) {
console.log('Service is down, using fallback');
}
`
`typescript
const metrics = client.getMetrics();
console.log(metrics);
// {
// telemetry: {
// totalRequests: 1000,
// successfulRequests: 950,
// failedRequests: 50,
// retriedRequests: 75,
// totalRetries: 120,
// circuitBreakerTrips: 2,
// averageLatency: 245.5,
// successRate: "95.00",
// retryRate: "7.50",
// p95Latency: 450,
// p99Latency: 890
// },
// circuitBreaker: {
// state: "CLOSED",
// failures: 0,
// successCount: 0
// },
// dlq: {
// totalFailed: 50,
// byStatus: {...},
// byEndpoint: {...}
// }
// }
// Listen to telemetry events
client.telemetry.on('metric', ({ success, latency, retries }) => {
console.log(Request: ${success ? 'โ
' : 'โ'} ${latency}ms (${retries} retries));`
});
typescript
const client = new ElasticReqClient({
strategy: RetryStrategy.EXPONENTIAL,
initialDelayMs: 1000,
maxDelayMs: 30000,
});
// Delays: 1s, 2s, 4s, 8s, 16s, 30s (capped)
`$3
`typescript
const client = new ElasticReqClient({
strategy: RetryStrategy.LINEAR,
initialDelayMs: 1000,
});
// Delays: 1s, 2s, 3s, 4s, 5s...
`$3
`typescript
const client = new ElasticReqClient({
strategy: RetryStrategy.FIBONACCI,
initialDelayMs: 1000,
});
// Delays: 1s, 1s, 2s, 3s, 5s, 8s, 13s...
`$3
`typescript
const client = new ElasticReqClient({
strategy: RetryStrategy.JITTERED,
initialDelayMs: 1000,
jitterFactor: 0.2, // ยฑ20% randomness
});
// Delays: ~1s, ~2s, ~4s (with random variance)
`๐ง Memory Optimization
The client is designed for high-traffic scenarios:
- Bounded DLQ: LRU eviction when max size reached (default: 1000 entries)
- Metrics Caching: DLQ metrics cached for 5 seconds under load
- Latency History: Limited to 1000 most recent requests
- Fibonacci Cache: Limited to 50 entries for memory efficiency
- Circuit Breaker: Time-based failure tracking with automatic cleanup
๐ก๏ธ Error Handling
`typescript
try {
const response = await client.get('/users');
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.code === 'CIRCUIT_BREAKER_OPEN') {
console.error('Service unavailable - circuit breaker is open');
} else if (error.response) {
console.error(HTTP ${error.response.status}: ${error.response.data});
} else {
console.error('Network error:', error.message);
}
}
}
`๐งน Cleanup
`typescript
// Clean up resources when done
client.destroy();
`๐ Default Configuration
`typescript
{
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 30000,
strategy: RetryStrategy.EXPONENTIAL,
jitterFactor: 0.1,
retryableStatuses: [408, 429, 500, 502, 503, 504],
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED', 'ENETUNREACH'],
timeoutMs: 5000,
circuitBreaker: {
failureThreshold: 5,
resetTimeoutMs: 60000,
monitoringWindowMs: 10000
},
dlq: {
maxSize: 1000,
enableMetrics: true
}
}
``Contributions are welcome! Please feel free to submit a Pull Request.
MIT ยฉ
Built with Axios and inspired by enterprise resilience patterns.