Hone logo
Hone
Problems

Implementing Robust Retry Logic for HTTP Requests in Angular

Many real-world applications interact with external APIs. These APIs can sometimes be temporarily unavailable or experience network issues, leading to failed HTTP requests. Implementing a robust retry mechanism for these requests is crucial for improving the resilience and user experience of your Angular application. This challenge will guide you through building such a system.

Problem Description

Your task is to create an Angular service that intercepts outgoing HTTP requests and automatically retries them if they fail due to transient errors. This service should be configurable to control the number of retries and the delay between them.

Key Requirements:

  • HTTP Interceptor: Implement an Angular HttpInterceptor to hook into the HTTP request pipeline.
  • Retry on Specific Errors: Only retry requests that fail due to specific, retryable error codes (e.g., 500 Internal Server Error, 503 Service Unavailable, network errors). Do not retry for client-side errors (e.g., 400 Bad Request, 401 Unauthorized).
  • Configurable Retries: Allow configuring the maximum number of retry attempts for each request.
  • Configurable Delay: Allow configuring the delay (in milliseconds) between retry attempts. A common pattern is exponential backoff, but for this challenge, a fixed delay is sufficient.
  • Return Original Observable: The interceptor should return the original observable of the HTTP request, potentially modified to include retry logic.
  • Handle Success: If a request succeeds after retries, return its successful response.
  • Handle Final Failure: If a request fails after all retry attempts, re-throw the error.

Expected Behavior:

When a request fails with a retryable error, the interceptor should wait for the specified delay and then resend the request. This process repeats up to the maximum number of retries.

Edge Cases to Consider:

  • Requests that succeed on the first attempt should not be affected by the retry logic.
  • Requests that fail with non-retryable errors should be immediately propagated without retries.
  • The interceptor should not interfere with other existing interceptors in the application.

Examples

Example 1: Successful Request

Input: A GET request to '/api/data' which succeeds on the first attempt.
Output: The response data from '/api/data'.
Explanation: The request is made, it succeeds, and the response is returned immediately. No retries are initiated.

Example 2: Request that Fails and Succeeds on Retry

Input: A POST request to '/api/submit' configured with 2 retries and a 500ms delay.
- Attempt 1: Fails with a 500 Internal Server Error.
- Delay: 500ms
- Attempt 2: Succeeds.

Output: The success response data from '/api/submit'.
Explanation: The first attempt fails. After a 500ms delay, the request is retried. The second attempt succeeds, and its response is returned.

Example 3: Request that Fails After All Retries

Input: A GET request to '/api/status' configured with 1 retry and a 200ms delay.
- Attempt 1: Fails with a 503 Service Unavailable.
- Delay: 200ms
- Attempt 2: Fails with a 500 Internal Server Error.

Output: An error object representing the final 500 Internal Server Error.
Explanation: The first attempt fails. After a 200ms delay, the request is retried. The second attempt also fails. Since all retries are exhausted, the error from the last attempt is thrown.

Constraints

  • The retry logic should be implemented as an Angular HttpInterceptor.
  • The maximum number of retries should be configurable, with a reasonable default (e.g., 3).
  • The delay between retries should be configurable in milliseconds, with a reasonable default (e.g., 1000ms).
  • Retryable error status codes include 500, 503, and any network-related errors.
  • Non-retryable error status codes include 400, 401, 403, 404, etc.
  • The solution must be written in TypeScript.

Notes

  • You will need to use RxJS operators such as retry, delay, take, catchError, and potentially mergeMap or switchMap.
  • Consider how to pass retry configuration to the interceptor. You might achieve this by creating an injectable service or by configuring the HttpClient module.
  • Think about how to distinguish between different types of errors in the catchError block. HttpErrorResponse will be your primary tool here.
  • Success looks like a well-architected Angular service that gracefully handles transient API failures without compromising the user experience.
Loading editor...
typescript