### Install retryyy with npm, pnpm, yarn, or bun Source: https://github.com/stefanmaric/retryyy/blob/main/README.md These commands show how to install the retryyy library using different package managers. Ensure you have Node.js and your chosen package manager installed. ```shell pnpm add retryyy ``` ```shell npm install retryyy ``` ```shell yarn add retryyy ``` ```shell bun add retryyy ``` -------------------------------- ### Bandwidth Optimization with Core Implementation Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This example utilizes the core implementation of retryyy (aliased as 'wrap') for bandwidth savings. It requires explicit policy definition, demonstrating a simple linear backoff strategy. ```typescript import { core as wrap } from 'retryyy/core' import type RetryPolicy from 'retryyy/core' const simpleExamplePolicy: RetryPolicy = ({ attempt, error }) => { // Give up after 3 tries. if (attempt > 3) { throw error } // Linear backoff, waits 1s, 2s, 3s, 4s, etc. return attempt * 1000 } export const fetchUser = wrap(async (id: number) => { // do stuff... }, simpleExamplePolicy) ``` -------------------------------- ### Default Policy Configuration and Customization in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt Shows how to use the pre-configured Default policy in retryyy for common production scenarios, including examples of customization for maximum attempts, timeouts, delays, and disabling logging. It also demonstrates chaining custom policies. ```typescript import { wrap } from 'retryyy' import { Default } from 'retryyy/policies' // Use default policy directly const fetchData = wrap( async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, Default() ) // Customize default policy const customDefault = Default({ maxAttempts: 5, // Default: 10 timeout: 15_000, // Default: 30_000 (30s) initialDelay: 200, // Default: 150 maxDelay: 10_000, // Default: 30_000 (30s) fastTrack: true, // Default: false logWarn: console.log, // Default: console.warn logError: console.error, // Default: console.error }) // Disable logging const silentDefault = Default({ logWarn: false, logError: false, maxAttempts: 3, timeout: 10_000 }) // Add custom policy after default policies import type { RetryPolicy } from 'retryyy' const customErrorHandler: RetryPolicy = ({ error }) => { if (error instanceof Error && error.message.includes('AUTH')) { // Don't retry authentication errors throw error } } const enhancedDefault = Default({ maxAttempts: 5, next: customErrorHandler // Chain custom policy after defaults }) // Default policy is equivalent to: // join( // Logger({ warn: console.warn, error: console.error }), // Timeout({ after: 30_000 }), // Breaker({ max: 10 }), // PollyJitter({ initial: 150, max: 30_000 }) // ) // Using Default via wrap options (most common) const fetchUser = wrap( async (id: number) => { const res = await fetch(`https://api.example.com/users/${id}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, { maxAttempts: 5, timeout: 10_000, fastTrack: true, logWarn: false } ) const user = await fetchUser(123) console.log(user) ``` -------------------------------- ### Compose Retry Policies with Join Source: https://context7.com/stefanmaric/retryyy/llms.txt Demonstrates composing multiple retry policies using the `join` function. This example chains a logger, a circuit breaker, jitter, and exponential backoff to create a robust retry strategy. Each policy can modify behavior or delegate to the next. ```typescript import type { RetryPolicy } from 'retryyy' import { join, retryyy } from 'retryyy' // Circuit breaker: stop after max attempts const breaker: RetryPolicy = (state, next) => { if (state.attempt > 5) { throw state.error } // Pass control to next policy return next ? next(state) : 0 } // Exponential backoff calculator const backoff: RetryPolicy = (state) => { return Math.pow(2, state.attempt - 1) * 1000 } // Jitter: add randomness to prevent thundering herd const jitter: RetryPolicy = (state, next) => { if (!next) { throw state.error } const delay = next(state) // Add up to 1 second of random jitter return delay + Math.random() * 1000 } // Logger: track retry attempts const logger: RetryPolicy = (state, next) => { console.warn(`Attempt ${state.attempt} failed, elapsed: ${state.elapsed}ms`) try { return next ? next(state) : 0 } catch (error) { console.error(`Gave up after ${state.attempt} attempts`) throw error } } // Compose policies: logger → breaker → jitter → backoff const composedPolicy = join(logger, breaker, jitter, backoff) await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, composedPolicy) ``` -------------------------------- ### Customize retryyy default policy options Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This example shows how to customize the behavior of the default retry policy by passing an options object as the second argument to `retryyy()`. You can adjust settings like `timeout`. ```javascript import { retryyy } from 'retryyy' retryyy( async () => { // do stuff... }, { timeout: 10_000, // Shorter timeout; 10 seconds. }, ) ``` -------------------------------- ### Customize Retry Behavior with Options (TypeScript) Source: https://context7.com/stefanmaric/retryyy/llms.txt This example shows how to customize the retry behavior of the retryyy library by providing a configuration object. Options include setting the maximum number of attempts, timeout duration, initial and maximum delay between retries, enabling fast-track retries, and controlling warning and error logging. This allows fine-tuning the retry strategy for specific asynchronous operations. ```typescript await retryyy( async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, { maxAttempts: 5, timeout: 10_000, initialDelay: 200, maxDelay: 5000, fastTrack: true, logWarn: false, logError: console.error, } ) ``` -------------------------------- ### Retry with AbortSignal for Cancellation (TypeScript) Source: https://context7.com/stefanmaric/retryyy/llms.txt This example demonstrates how to use an `AbortSignal` with retryyy to enable cancellation of the retry process. An `AbortController` is created, and its signal is passed to both the `fetch` request and the `retryyy` function. A timeout is set to abort the operation, allowing the `try...catch` block to handle the cancellation or any resulting errors. ```typescript const controller = new AbortController() setTimeout(() => controller.abort(), 5000) // Cancel after 5 seconds try { await retryyy( async () => { const res = await fetch('https://api.example.com/data', { signal: controller.signal }) return await res.json() }, { maxAttempts: 20 }, controller.signal ) } catch (error) { console.error('Operation cancelled or failed:', error) } ``` -------------------------------- ### Implement Jitter Policies for Retry Decorrelation in retryyy Source: https://context7.com/stefanmaric/retryyy/llms.txt Introduces various jitter policies (Full, Equal, Decorrelated, Polly) to randomize retry delays and prevent synchronized retries in distributed systems. It shows custom jitter configuration and provides an example for a high-traffic API client using PollyJitter. ```typescript import { wrap, join } from 'retryyy' import { Jitter, FullJitter, EqualJitter, DecorrelatedJitter, PollyJitter, Backoff, Breaker } from 'retryyy/policies' // Custom jitter with range and offset const customJitter = Jitter({ range: -0.5, // Window size: 50% of delay offset: 0.25 // Shift window right by 25% }) // Full jitter: randomize between 0 and full delay // Example: 4s delay → random between 0-4s const fullJitter = join( Breaker({ max: 5 }), FullJitter(), Backoff({ delay: 1000 }) ) // Equal jitter: delay/2 + random(0, delay/2) // Example: 4s delay → random between 2-6s const equalJitter = join( Breaker({ max: 5 }), EqualJitter(), Backoff({ delay: 1000 }) ) // Decorrelated jitter: AWS-style decorrelated backoff // Prevents thundering herd in distributed systems const decorrelatedJitter = DecorrelatedJitter({ initial: 150, max: 30_000 }) // PollyJitter: Advanced jitter from Polly library (RECOMMENDED) // Best for high-throughput production systems // Maintains smooth distribution with controlled median delay const pollyJitter = PollyJitter({ initial: 150, // Median first delay max: 30_000 // Maximum delay cap }) // Example: High-traffic API client const apiClient = wrap( async (endpoint: string) => { const res = await fetch(`https://api.example.com/${endpoint}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, join( Breaker({ max: 10 }), PollyJitter({ initial: 200, max: 10_000 }) ) ) // Example: Compare different jitter strategies const policies = { full: join(Breaker({ max: 3 }), FullJitter(), Backoff()), equal: join(Breaker({ max: 3 }), EqualJitter(), Backoff()), decorrelated: DecorrelatedJitter({ initial: 150, max: 5000 }), polly: PollyJitter({ initial: 150, max: 5000 }) } for (const [name, policy] of Object.entries(policies)) { const fetchFn = wrap( async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, policy ) console.log(`Testing ${name} jitter strategy`) try { await fetchFn() } catch (error) { console.error(`${name} failed:`, error) } } ``` -------------------------------- ### Disable logging in retryyy operations Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This example demonstrates how to disable both error and warning logs during retry operations by setting `logError` and `logWarn` to `false`. This is useful for cleaner output in production environments. ```javascript import { retryyy } from 'retryyy' retryyy( async () => { // do stuff... }, { logError: false, logWarn: false, }, ) ``` -------------------------------- ### Wrap functions with retry logic using retryyy.wrap Source: https://github.com/stefanmaric/retryyy/blob/main/README.md The `wrap` API provides a way to create new functions with retry logic attached, promoting cleaner code composition. This example wraps a `_fetchUser` function with a specific timeout. ```typescript import { wrap } from 'retryyy' type UserShape = { id: number; name: string } async function _fetchUser(id: number) { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) return (await res.json()) as UserShape } export const fetchUser = wrap(_fetchUser, { timeout: 10_000 }) const user = await fetchUser(1) console.log(user) ``` -------------------------------- ### Implement Custom Retry Policies in TypeScript with Retryyy Source: https://github.com/stefanmaric/retryyy/blob/main/README.md Demonstrates how to define and use a custom retry policy with the `retryyy` function. A custom policy is a function that receives the retry state and returns a delay in milliseconds or throws an error to stop retrying. This example implements a policy that stops after 3 attempts or 5 seconds. ```typescript import type { RetryPolicy } from 'retryyy' import { retryyy } from 'retryyy' const customPolicy: RetryPolicy = (state) => { if (state.attempt > 3 || state.elapsed > 5_000) { throw state.error } return state.attempt * 1000 } type UserShape = { id: number; name: string } retryyy(async () => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${1}`) const user = await res.json() console.log(user) }, customPolicy) ``` -------------------------------- ### Cancel Operations with AbortSignal Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This example demonstrates how to use AbortSignal to cancel ongoing operations initiated by retryyy. It includes event listeners for form submission and a cancel button, passing the signal to both fetch and retryyy. ```typescript import { retryyy } from 'retryyy' let controller: AbortController | null = null const handleSubmit = (event: SubmitEvent) => { event.preventDefault() if (controller) { // Do not restart the request if it is already in progress. return } try { controller = new AbortController() retryyy( async () => { const res = await fetch( `https://jsonplaceholder.typicode.com/users/1`, { signal: controller?.signal }, // Pass the signal to the fetch call. ) const user = await res.json() console.log(user) }, { // Pass an empty object if you don't need to customize the default policy. }, controller.signal, // Pass the signal to the retryyy call. ) } finally { controller = null } } const handleCancel = (event: MouseEvent) => { if (controller) { controller.abort(new Error('Request cancelled by the user')) } } document.querySelector('form').addEventListener('submit', handleSubmit) document.querySelector('.cancel-btn').addEventListener('click', handleCancel) ``` -------------------------------- ### Logger Policy for Retry Monitoring in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt The Logger policy logs retry attempts and final failures to the console or custom logging functions. It is useful for debugging and monitoring retry behavior. It can be configured with default console logging or custom functions for more advanced logging setups, including integration with structured logging libraries like Winston. Dependencies include the 'retryyy' library. ```typescript import { wrap, join } from 'retryyy' import { Logger, Breaker, Backoff } from 'retryyy/policies' // Default logger (uses console.warn and console.error) const defaultLogger = Logger() // Custom logger with custom functions const customLogger = Logger({ warn: (message: string, error: unknown) => { console.log(`[RETRY] ${message}`, error) }, error: (message: string, error: unknown) => { console.error(`[FAILED] ${message}`, error) } }) // Use with policy chain const policy = join( Logger(), // Log attempts and failures Breaker({ max: 5 }), Backoff({ delay: 500 }) ) const fetchData = wrap( async (url: string) => { const res = await fetch(url) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, policy ) // Example output: // [retryyy] Attempt 1 failed after 234ms Error: HTTP 503 // [retryyy] Attempt 2 failed after 1456ms Error: HTTP 503 // [retryyy] Attempt 3 failed after 3789ms Error: HTTP 503 // [retryyy] Giving up after 3 attempts and 3789ms Error: HTTP 503 // Integration with structured logging import { Logger as WinstonLogger, createLogger, format, transports } from 'winston' const winstonLogger = createLogger({ format: format.json(), transports: [new transports.Console()] }) const structuredLogger = Logger({ warn: (message: string, error: unknown) => { winstonLogger.warn(message, { error }) }, error: (message: string, error: unknown) => { winstonLogger.error(message, { error }) } }) const robustFetch = wrap( async (endpoint: string) => { const res = await fetch(`https://api.example.com/${endpoint}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, join( structuredLogger, Breaker({ max: 3 }), Backoff() ) ) ``` -------------------------------- ### Compose Advanced Retry Policies with Join Source: https://context7.com/stefanmaric/retryyy/llms.txt Illustrates a complex composition of retry policies including timeout, circuit breaker, rate limiter with conditional delays, jitter, and capped exponential backoff. These policies are chained using `join` for sophisticated retry logic. ```typescript import type { RetryPolicy } from 'retryyy' import { join, retryyy } from 'retryyy' // Timeout policy const timeout: RetryPolicy = (state, next) => { if (state.elapsed > 30_000) { throw new Error(`Timeout after ${state.elapsed}ms`) } return next ? next(state) : 0 } // Circuit breaker: stop after max attempts const breaker: RetryPolicy = (state, next) => { if (state.attempt > 5) { throw state.error } return next ? next(state) : 0 } // Rate limiter: use longer delays for specific errors const rateLimiter: RetryPolicy = (state, next) => { // For specific error types, use longer delays if (state.error instanceof Error && state.error.message.includes('rate limit')) { return 60_000 // Wait 1 minute for rate limit errors } return next ? next(state) : 1000 } // Jitter: add randomness to prevent thundering herd const jitter: RetryPolicy = (state, next) => { if (!next) { throw state.error } const delay = next(state) // Add up to 1 second of random jitter return delay + Math.random() * 1000 } // Capped backoff calculator const cappedBackoff: RetryPolicy = (state) => { // Cap maximum delay at 10 seconds return Math.min(10_000, Math.pow(2, state.attempt) * 500) } // Chain: timeout → breaker → rateLimiter → jitter → cappedBackoff const advancedPolicy = join(timeout, breaker, rateLimiter, jitter, cappedBackoff) await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, advancedPolicy) ``` -------------------------------- ### FastTrack Policy for Immediate First Retry in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt The FastTrack policy executes the first retry attempt immediately without delay, then delegates to the next policy for subsequent attempts. This is useful for transient network glitches. It can be used within a policy chain or enabled via options in the `wrap` function. Dependencies include the 'retryyy' library. ```typescript import { wrap, join } from 'retryyy' import { FastTrack, Breaker, Backoff } from 'retryyy/policies' // Policy with immediate first retry const fastTrackPolicy = join( Breaker({ max: 5 }), FastTrack(), // First retry happens immediately Backoff({ delay: 1000 }) // Subsequent retries: 1s, 2s, 4s... ) const fetchUser = wrap( async (id: number) => { const res = await fetch(`https://api.example.com/users/${id}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, fastTrackPolicy ) // Timeline without FastTrack: // Attempt 1: fails at 0ms // Attempt 2: retry at 1000ms (1s delay) // Attempt 3: retry at 3000ms (2s delay) // Timeline with FastTrack: // Attempt 1: fails at 0ms // Attempt 2: retry at 0ms (immediate) // Attempt 3: retry at 1000ms (1s delay) // Attempt 4: retry at 3000ms (2s delay) // Use with default policy via options import { wrap } from 'retryyy' const fetchWithFastTrack = wrap( async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, { fastTrack: true, // Enable fast track in default policy maxAttempts: 5, initialDelay: 500 } ) // Useful for network requests prone to transient failures const apiCall = wrap( async (endpoint: string, data: object) => { const res = await fetch(`https://api.example.com/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, join( Breaker({ max: 3 }), FastTrack(), Backoff({ delay: 2000 }) ) ) try { const result = await apiCall('orders', { item: 'book', qty: 1 }) console.log('Order created:', result) } catch (error) { console.error('Failed to create order:', error) } ``` -------------------------------- ### Class Method Decorators with Retry Logic (TypeScript) Source: https://context7.com/stefanmaric/retryyy/llms.txt This code demonstrates applying retry logic to class methods using ECMAScript decorators with the `Retryyy` class from the `retryyy` library. This approach integrates seamlessly with TypeScript 5.0+ and ensures proper context binding for class methods. ```typescript import { Retryyy } from 'retryyy' type User = { id: number; name: string } type Order = { id: string; userId: number; total: number } class UserService { private baseUrl = 'https://api.example.com' @Retryyy({ timeout: 10_000, maxAttempts: 5 }) async fetchUser(id: number): Promise { const res = await fetch(`${this.baseUrl}/users/${id}`) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } return await res.json() as User } @Retryyy({ timeout: 20_000, maxAttempts: 3, initialDelay: 500 }) async updateUser(id: number, data: Partial): Promise { const res = await fetch(`${this.baseUrl}/users/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (!res.ok) { throw new Error(`Update failed: ${res.status}`) } return await res.json() as User } } // Use the decorated methods const service = new UserService() const user = await service.fetchUser(123) console.log(user.name) // Create reusable decorators const RetryForever = Retryyy({ maxAttempts: Infinity, timeout: Infinity }) const RetryQuick = Retryyy({ maxAttempts: 3, timeout: 5000, fastTrack: true }) class OrderService { @RetryForever async syncOrders(): Promise { // Critical sync operation that must succeed const res = await fetch('https://api.example.com/orders/sync') if (!res.ok) throw new Error('Sync failed') } @RetryQuick async getOrderStatus(orderId: string): Promise { const res = await fetch(`https://api.example.com/orders/${orderId}`) return await res.json() as Order } } // Alternative: class field syntax with wrap import { wrap } from 'retryyy' class PaymentService { processPayment = wrap(async (amount: number, token: string) => { const res = await fetch('https://api.example.com/payments', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount, token }) }) return await res.json() }, { maxAttempts: 3, timeout: 15_000 }) } ``` -------------------------------- ### Lightweight Core Retry Implementation in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt Implements a minimal retry logic in TypeScript, allowing for explicit definition of retry policies. It's suitable for bandwidth-constrained environments or custom retry logic, requiring explicit policy definition. It takes a function to retry and a policy function as input. ```typescript import { core as wrap } from 'retryyy/core' import type { RetryPolicy } from 'retryyy/core' // Define a simple policy const simplePolicy: RetryPolicy = ({ attempt, error }) => { // Give up after 3 tries if (attempt > 3) { throw error } // Linear backoff: 1s, 2s, 3s return attempt * 1000 } // Wrap a function with the core implementation type User = { id: number; name: string } const fetchUser = wrap(async (id: number) => { const res = await fetch(`https://api.example.com/users/${id}`) if (!res.ok) { throw new Error(`HTTP ${res.status}`) } return await res.json() as User }, simplePolicy) const user = await fetchUser(123) console.log(user.name) // Production-ready policy with exponential backoff and jitter const productionPolicy: RetryPolicy = ({ attempt, error, elapsed }) => { // Circuit breaker: max 10 attempts if (attempt >= 10) { throw error } // Timeout: give up after 30 seconds if (elapsed > 30_000) { throw error } // Exponential backoff with jitter const exponentialDelay = Math.pow(2, attempt - 1) * 1000 const jitter = Math.random() * 500 const delay = Math.min(30_000, exponentialDelay + jitter) return delay } // Advanced policy using state const adaptivePolicy: RetryPolicy = ({ attempt, error, elapsed, errors, delay }) => { if (attempt > 5 || elapsed > 20_000) { throw error } // Analyze error patterns const hasRateLimitError = errors.some(e => e instanceof Error && e.message.includes('429') ) if (hasRateLimitError) { // Use longer delays for rate limiting return delay * 2 || 5000 } // Standard exponential backoff return Math.pow(2, attempt - 1) * 500 } const robustFetch = wrap( async (url: string, options?: RequestInit) => { const res = await fetch(url, options) if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`) } return await res.json() }, adaptivePolicy ) try { const data = await robustFetch('https://api.example.com/data') console.log('Data fetched:', data) } catch (error) { console.error('All retry attempts failed:', error) } ``` -------------------------------- ### Create Reusable Retry Policies with Retryyy Factory in TypeScript Source: https://github.com/stefanmaric/retryyy/blob/main/README.md Illustrates how to create a reusable retry policy factory using `Retryyy`. This allows defining a specific retry configuration (e.g., infinite attempts, infinite timeout) once and applying it to multiple methods across different classes using the decorator syntax. ```typescript import { Retryyy } from 'retryyy' const RetryForever = Retryyy({ maxAttempts: Infinity, timeout: Infinity }) class UserModel { @RetryForever async fetchUser(id: number) { // do stuff... } @RetryForever async deleteUser(id: number) { // do stuff... } } class CartModel { @RetryForever async clearCart() { // do stuff... } } ``` -------------------------------- ### Compose Retry Policies using `join()` in TypeScript with Retryyy Source: https://github.com/stefanmaric/retryyy/blob/main/README.md Explains how to compose multiple retry policies together using the `join()` function provided by retryyy. This allows creating complex retry strategies by chaining simpler policies, such as a circuit breaker, a jittered exponential backoff, and other custom logic. Policies are executed sequentially. ```typescript import type { RetryPolicy } from 'retryyy' import { join, retryyy } from 'retryyy' /* 1 */ const breaker: RetryPolicy = (state, next) => { if (state.attempt > 5) { throw state.error } return next(state) } /* 3 */ const jitter: RetryPolicy = (state, next) => { const delay = next(state) return delay + Math.random() * 1000 } /* 2 */ const backoff: RetryPolicy = (state) => { return Math.pow(2, state.attempt - 1) * 1000 } const composedPolicy = join(breaker, jitter, backoff) type UserShape = { id: number; name: string } retryyy(async () => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${1}`) const user = await res.json() console.log(user) }, composedPolicy) ``` -------------------------------- ### Execute Async Function with Default Retry Policy (TypeScript) Source: https://context7.com/stefanmaric/retryyy/llms.txt This snippet demonstrates the basic usage of the retryyy library to execute an asynchronous function with its default retry policy. This policy includes exponential backoff with Polly jitter, a circuit breaker with a maximum of 10 attempts, a 30-second timeout, and console logging. The function is retried automatically if it throws an error, such as an HTTP error. ```typescript import { retryyy } from 'retryyy' // Basic usage with default retry policy await retryyy(async () => { const res = await fetch('https://api.example.com/users/1') if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`) } const user = await res.json() console.log(`User: ${user.name}`) return user }) ``` -------------------------------- ### Implement Rate-Limit Aware Retry Policy Source: https://context7.com/stefanmaric/retryyy/llms.txt Creates a retry policy that is aware of rate limiting errors. It includes custom error handling for `RateLimitError`, honoring a `retryAfter` value, and falls back to a default exponential backoff for other errors. The policy also has a maximum attempt limit. ```typescript import type { RetryPolicy } from 'retryyy' import { retryyy } from 'retryyy' // Policy with error-specific handling class RateLimitError extends Error { constructor(public retryAfter: number) { super('Rate limited') } } const rateLimitAware: RetryPolicy = (state) => { if (state.attempt > 5) { throw state.error } // Honor rate limit retry-after header if (state.error instanceof RateLimitError) { return state.error.retryAfter * 1000 } // Default exponential backoff return Math.min(30_000, Math.pow(2, state.attempt) * 1000) } await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, rateLimitAware) ``` -------------------------------- ### Configure Timeout Policies in retryyy Source: https://context7.com/stefanmaric/retryyy/llms.txt Sets up timeout policies to abort retry attempts after a specified duration. It supports default, custom durations, and combination with other policies like Breaker and Backoff. It also illustrates using AbortSignal for hard timeouts. ```typescript import { wrap, join } from 'retryyy' import { Timeout, Breaker, Backoff } from 'retryyy/policies' // Default 30-second timeout const defaultTimeout = Timeout() // Custom 10-second timeout const shortTimeout = Timeout({ after: 10_000 }) // Very long timeout for critical operations const longTimeout = Timeout({ after: 300_000 }) // 5 minutes // Combine with other policies const policy = join( Timeout({ after: 15_000 }), // Give up after 15 seconds total Breaker({ max: 10 }), // Or after 10 attempts Backoff({ delay: 500 }) ) const fetchData = wrap( async (url: string) => { const res = await fetch(url) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, policy ) try { // Will retry for up to 15 seconds const data = await fetchData('https://api.example.com/slow-endpoint') console.log(data) } catch (error) { console.error('Operation timed out or failed:', error) } // Note: For hard timeouts that cancel in-flight operations, // use AbortSignal in your async function const fetchWithAbort = wrap( async (url: string, signal?: AbortSignal) => { const res = await fetch(url, { signal }) return await res.json() }, Timeout({ after: 5_000 }) ) const controller = new AbortController() const result = await fetchWithAbort(controller.signal)( 'https://api.example.com/data', controller.signal ) ``` -------------------------------- ### Wrap Class Methods with Retryyy using Class Field Syntax in TypeScript Source: https://github.com/stefanmaric/retryyy/blob/main/README.md Shows an alternative method for applying retry logic to class methods using class field syntax with the `wrap` function from retryyy. This approach assigns the wrapped function directly to the class field, which can affect `this` binding and instance performance compared to prototype methods. ```typescript import { wrap } from 'retryyy' type UserShape = { id: number; name: string } class UserModel { fetchUser = wrap(async (id: number) => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) return (await res.json()) as UserShape }) } ``` -------------------------------- ### Implement Time-Budgeted Retry Policy Source: https://context7.com/stefanmaric/retryyy/llms.txt Implements a time-based retry policy that gives up after a total elapsed time or a maximum number of attempts. It uses exponential backoff for delays between retries. The policy logs a message before throwing an error if the time budget or attempt limit is exceeded. ```typescript import type { RetryPolicy } from 'retryyy' import { retryyy } from 'retryyy' // Time-based policy with conditional retry const timeBudgetPolicy: RetryPolicy = (state) => { // Give up after 5 seconds total elapsed time if (state.elapsed > 5_000) { console.log(`Giving up after ${state.elapsed}ms and ${state.attempt} attempts`) throw state.error } // Give up after 3 attempts if (state.attempt > 3) { throw state.error } // Exponential backoff: 500ms, 1000ms, 2000ms return Math.pow(2, state.attempt - 1) * 500 } await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, timeBudgetPolicy) ``` -------------------------------- ### Exponential Backoff Policy in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt Provides a built-in exponential backoff policy for retryyy. It generates exponentially increasing delays between retry attempts with configurable base delay, exponent, and maximum delay cap. This policy can be used standalone or composed with other policies. ```typescript import { wrap } from 'retryyy' import { Backoff } from 'retryyy/policies' // Default exponential backoff (base: 150ms, exp: 2, max: 30s) const fetchWithBackoff = wrap( async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, Backoff() ) // Custom exponential backoff const customBackoff = Backoff({ delay: 500, // Start at 500ms exp: 2, // Double each time: 500ms, 1s, 2s, 4s, 8s... max: 10_000 // Cap at 10 seconds }) // Linear backoff using exp: 1 const linearBackoff = Backoff({ delay: 1000, exp: 1, // Linear: 1s, 2s, 3s, 4s, 5s... max: 5000 }) // Aggressive exponential backoff const aggressiveBackoff = Backoff({ delay: 100, exp: 3, // Triple each time: 100ms, 300ms, 900ms, 2.7s... max: 30_000 }) // Use with join for composition import { join } from 'retryyy' import { Breaker } from 'retryyy/policies' const policy = join( Breaker({ max: 5 }), Backoff({ delay: 200, exp: 2, max: 10_000 }) ) const fetchData = wrap( async (endpoint: string) => { const res = await fetch(`https://api.example.com/${endpoint}`) return await res.json() }, policy ) ``` -------------------------------- ### Implement Linear Backoff Retry Policy Source: https://context7.com/stefanmaric/retryyy/llms.txt Defines a simple linear backoff retry policy where the delay increases linearly with each attempt. The policy throws an error if the number of attempts exceeds a threshold, aborting the operation. It takes retry state as input and returns the delay in milliseconds. ```typescript import type { RetryPolicy } from 'retryyy' import { retryyy } from 'retryyy' // Simple linear backoff policy const linearBackoff: RetryPolicy = (state) => { if (state.attempt > 3) { throw state.error } // Wait 1s, 2s, 3s between retries return state.attempt * 1000 } await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, linearBackoff) ``` -------------------------------- ### Implement Monitored Retry Policy with Logging Source: https://context7.com/stefanmaric/retryyy/llms.txt A retry policy that includes side effects like logging retry attempts and critical failures. It logs metrics for each attempt and logs a critical error with details if max retries are exceeded. It uses exponential backoff for delays. ```typescript import type { RetryPolicy } from 'retryyy' import { retryyy } from 'retryyy' // Policy with side effects (logging, metrics) const monitoredRetry: RetryPolicy = (state) => { // Send metrics to monitoring system console.log(`Retry attempt ${state.attempt}, elapsed: ${state.elapsed}ms`) if (state.attempt > 10) { // Log critical failure console.error('Max retries exceeded', { attempts: state.attempt, elapsed: state.elapsed, errors: state.errors }) throw state.error } return Math.pow(2, state.attempt - 1) * 1000 } await retryyy(async () => { const res = await fetch('https://api.example.com/data') return await res.json() }, monitoredRetry) ``` -------------------------------- ### Wrap Function with Composed Retry Policy Source: https://context7.com/stefanmaric/retryyy/llms.txt Utilizes the `wrap` function from `retryyy` to apply a composed retry policy to an existing asynchronous function. This creates a reusable, retrying version of the function, simplifying its usage in the application. ```typescript import type { RetryPolicy } from 'retryyy' import { join, retryyy, wrap } from 'retryyy' // ... (definitions for timeout, breaker, rateLimiter, jitter, cappedBackoff as above) ... const advancedPolicy = join(timeout, breaker, rateLimiter, jitter, cappedBackoff) // Use composed policy with wrap for reusable functions const fetchWithRetry = wrap( async (url: string) => { const res = await fetch(url) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, advancedPolicy ) const data = await fetchWithRetry('https://api.example.com/endpoint') ``` -------------------------------- ### Basic usage of retryyy for async operations Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This snippet demonstrates the fundamental usage of the `retryyy` function to wrap an asynchronous operation. It will automatically apply default retry policies to the provided async function. ```javascript import { retryyy } from 'retryyy' retryyy(async () => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${1}`) const user = await res.json() console.log(user) }) ``` -------------------------------- ### Error Aggregation with BrandError Policy in TypeScript Source: https://context7.com/stefanmaric/retryyy/llms.txt Demonstrates how to use the BrandError policy to wrap all retry errors in a RetryyyError, providing a complete history of accumulated errors from retry attempts. This is useful for detailed error tracking and analysis. ```typescript import { wrap, join } from 'retryyy' import { BrandError, RetryyyError, Breaker, Backoff } from 'retryyy/policies' // Add BrandError to policy chain const policy = join( BrandError(), // Wraps errors in RetryyyError Breaker({ max: 3 }), Backoff({ delay: 500 }) ) const fetchData = wrap( async (url: string) => { const res = await fetch(url) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() }, policy ) try { await fetchData('https://api.example.com/data') } catch (error) { if (error instanceof RetryyyError) { console.error('All retry attempts failed') console.error('Number of errors:', error.errors.length) // Access all accumulated errors error.errors.forEach((err, index) => { console.error(`Attempt ${index + 1}:`, err) }) // Access the final error console.error('Final error:', error.cause) } } // Example output structure /* All retry attempts failed Number of errors: 3 Attempt 1: Error: HTTP 503 Attempt 2: Error: HTTP 503 Attempt 3: Error: HTTP 503 Final error: Error: HTTP 503 */ // Use for detailed error tracking const robustFetch = wrap( async (endpoint: string) => { const res = await fetch(`https://api.example.com/${endpoint}`) if (!res.ok) { throw new Error(`HTTP ${res.status}: ${res.statusText}`) } return await res.json() }, join( BrandError(), Breaker({ max: 5 }), Backoff() ) ) try { const data = await robustFetch('users/123') console.log(data) } catch (error) { if (error instanceof RetryyyError) { // Send to error tracking service console.error('Retry failure details:', { totalAttempts: error.errors.length, errors: error.errors.map(e => e instanceof Error ? e.message : String(e)), finalError: error.cause }) } throw error } ``` -------------------------------- ### Wrap Async Functions with Retry Logic (TypeScript) Source: https://context7.com/stefanmaric/retryyy/llms.txt This snippet demonstrates how to wrap an asynchronous function with retry capabilities using the `wrap` function from the `retryyy` library. It allows configuration of retry attempts, timeouts, and custom error handling logic, making it suitable for operations like API requests. ```typescript import { wrap } from 'retryyy' // Define the base function type User = { id: number; name: string; email: string } async function _fetchUser(id: number): Promise { const res = await fetch(`https://api.example.com/users/${id}`) if (!res.ok) { throw new Error(`Failed to fetch user ${id}`) } return await res.json() as User } // Wrap with retry logic export const fetchUser = wrap(_fetchUser, { timeout: 10_000, maxAttempts: 5 }) // Use the wrapped function const user = await fetchUser(123) console.log(user.name) // Wrap with custom error handling class APIError extends Error { constructor(public statusCode: number, message: string) { super(message) } } async function _createOrder(orderId: string, data: object) { const res = await fetch(`https://api.example.com/orders/${orderId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (!res.ok) { throw new APIError(res.status, 'Order creation failed') } return await res.json() } export const createOrder = wrap(_createOrder, { maxAttempts: 3, next: ({ error }) => { // Don't retry on 4xx client errors if (error instanceof APIError && error.statusCode >= 400 && error.statusCode < 500) { throw error } } }) // Use with AbortSignal const abortController = new AbortController() const wrappedFetch = wrap( async (url: string, signal?: AbortSignal) => { const res = await fetch(url, { signal }) return await res.json() }, { maxAttempts: 5 } ) // Pass signal twice: once for retryyy, once for the function const result = await wrappedFetch(abortController.signal)( 'https://api.example.com/data', abortController.signal ) ``` -------------------------------- ### Implement Custom Retry Logic in JavaScript Source: https://github.com/stefanmaric/retryyy/blob/main/README.md This JavaScript code defines a flexible retry function that takes a function to execute and a policy function to determine the wait time between retries. It handles retries with customizable backoff strategies, separating retry concerns from core business logic. Dependencies include a `wait` utility function for asynchronous delays. ```javascript const wait = (ms) => new Promise((resolve, reject) => { setTimeout(resolve, ms) }) export const retry = (fn, policy) => { return async (...args) => { const state = { attempt: 0, elapsed: 0, error: null, start: Date.now(), } while (true) { try { return await fn(...args) } catch (error) { state.attempt += 1 state.elapsed = Date.now() - state.start state.error = error await wait(policy(state)) } } } } ```