npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

elastic-req

v1.1.0

Published

Production-ready HTTP client with advanced retry strategies, circuit breaker, DLQ, and telemetry

Readme

🚀 Elastic Request HTTP Client

A production-ready, enterprise-grade HTTP client built on Axios with advanced retry strategies, circuit breaker pattern, dead letter queue (DLQ), and comprehensive telemetry.

✨ Features

  • 🔄 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

📦 Installation

npm install elastic-req
# or
yarn add elastic-req
# or
pnpm add elastic-req

🚀 Quick Start

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'
  }
});

📖 Complete Configuration

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.
  }
});

🎯 Usage Examples

Basic HTTP Requests

// GET request
const users = await client.get('/users');

// POST request
const newUser = await client.post('/users', {
  name: 'John Doe',
  email: '[email protected]'
});

// 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 with Generics

interface User {
  id: number;
  name: string;
  email: string;
}

const response = await client.get<User[]>('/users');
const users: User[] = response.data;

const newUser = await client.post<User>('/users', {
  name: 'John',
  email: '[email protected]'
});

Accessing Dead Letter Queue

// 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: { ... }
// }

Custom Persistence Adapter

import { PersistenceAdapter, DLQEntry } from 'elastic-req';

class RedisPersistenceAdapter implements PersistenceAdapter {
  async save(entry: DLQEntry): Promise<void> {
    await redis.set(`dlq:${entry.id}`, JSON.stringify(entry));
  }
  
  async load(limit?: number): Promise<DLQEntry[]> {
    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()
  }
});

Custom Retry Scheduler

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}`);
      } catch (err) {
        console.error(`❌ Retry failed for ${entry.id}`);
      }
    }, delay);
  }
}

const client = new ElasticReqClient({
  dlq: {
    retryScheduler: new CustomRetryScheduler()
  }
});

Circuit Breaker Events

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');
}

Telemetry and Metrics

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)`);
});

🎨 Retry Strategies

Exponential Backoff (Default)

const client = new ElasticReqClient({
  strategy: RetryStrategy.EXPONENTIAL,
  initialDelayMs: 1000,
  maxDelayMs: 30000,
});
// Delays: 1s, 2s, 4s, 8s, 16s, 30s (capped)

Linear Backoff

const client = new ElasticReqClient({
  strategy: RetryStrategy.LINEAR,
  initialDelayMs: 1000,
});
// Delays: 1s, 2s, 3s, 4s, 5s...

Fibonacci Backoff

const client = new ElasticReqClient({
  strategy: RetryStrategy.FIBONACCI,
  initialDelayMs: 1000,
});
// Delays: 1s, 1s, 2s, 3s, 5s, 8s, 13s...

Jittered Exponential

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

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

// Clean up resources when done
client.destroy();

📊 Default 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'],
  timeoutMs: 5000,
  
  circuitBreaker: {
    failureThreshold: 5,
    resetTimeoutMs: 60000,
    monitoringWindowMs: 10000
  },
  
  dlq: {
    maxSize: 1000,
    enableMetrics: true
  }
}

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT ©

🙏 Acknowledgments

Built with Axios and inspired by enterprise resilience patterns.