Hone logo
Hone
Problems

Robust API Request Retries with Exponential Backoff in Angular

Many applications rely on external APIs, which can be unreliable and occasionally fail due to network issues, server overload, or other transient problems. Implementing a retry mechanism with exponential backoff is crucial for building resilient applications that gracefully handle these failures. This challenge asks you to create a reusable Angular service that handles API requests with automatic retries and exponential backoff.

Problem Description

You need to create an RetryService in Angular that can be injected into components or services to handle API requests. This service should:

  1. Accept a function: The service should accept a function that performs the API request (e.g., an HttpClient call). This function should return a Promise.
  2. Implement Retry Logic: If the API request fails (rejects), the service should retry the request a specified number of times.
  3. Exponential Backoff: Between retries, the service should implement exponential backoff. This means the delay between retries increases exponentially (e.g., 1 second, 2 seconds, 4 seconds, 8 seconds...).
  4. Maximum Retries: The service should have a configurable maximum number of retries.
  5. Error Handling: After exceeding the maximum number of retries, the service should reject the Promise with the original error.
  6. Optional Initial Delay: Allow for an optional initial delay before the first retry attempt.
  7. Configurable Base Delay: Allow for a configurable base delay for the exponential backoff calculation.

Expected Behavior:

  • Successful API calls should resolve immediately.
  • Failed API calls should be retried according to the exponential backoff strategy.
  • After the maximum number of retries is reached, the Promise should reject with the original error.
  • The service should be reusable across different components and services.

Examples

Example 1:

Input:
retryService.retry( () => httpClient.get('/api/data'), 3, 1000, 2 ) // Retry 3 times, base delay 1000ms, initial delay 2ms

Output (on successful API call):
{ data: 'API Data' }

Explanation:
If the initial API call succeeds, the Promise resolves immediately with the data.

Example 2:

Input:
retryService.retry( () => httpClient.get('/api/failing-data'), 2, 500, 0 ) // Retry 2 times, base delay 500ms, no initial delay

Output (after 2 failed retries):
Error: Failed to fetch data after multiple retries. (Original API error)

Explanation:
The API call fails twice. The retries happen with delays of 500ms and 1000ms. After the second failure, the Promise rejects with the original error.

Example 3: (Edge Case - Initial Delay)

Input:
retryService.retry( () => httpClient.post('/api/submit', data), 1, 2000, 500 ) // Retry 1 time, base delay 2000ms, initial delay 500ms

Output (on successful API call):
{ success: true }

Explanation:
The API call is attempted. If it fails, there's a 500ms delay before the retry, followed by a 2000ms delay. If the retry succeeds, the Promise resolves.

Constraints

  • The base delay should be in milliseconds.
  • The maximum number of retries should be a positive integer.
  • The initial delay should be in milliseconds and non-negative.
  • The exponential backoff calculation should prevent excessively long delays (e.g., cap the delay at a reasonable maximum value like 30 seconds).
  • The service should be written in TypeScript and compatible with Angular.
  • The service should be designed to be easily testable.

Notes

  • Consider using setTimeout for implementing the delays.
  • Think about how to handle the original error and propagate it correctly after all retries have failed.
  • The HttpClient is assumed to be available. You don't need to mock it, but your code should be structured to easily accommodate it.
  • Focus on creating a clean, reusable, and well-documented service. Error messages should be informative.
  • The exponential backoff calculation can be implemented as baseDelay * 2^retryCount. Remember to cap the delay.
Loading editor...
typescript