Automatically retry failing HTTP requests | Angular
Things don’t always go as planned, sometime an unexpected event occurs that pushes you off the track. This is an evergreen scenario on the web.
Imagine you are on a train using your smartphone to fill out an important form and just when you hit the submit button, then train enters a tunnel and you loose connectivity: the request will likely fail and it could lead to an ugly looking error page, which let’s be honest is not the ideal user experience.
This is fairly common scenario for HTTP requests sent from a front-end application, a request could fail for a number of reasons, such as:
- temporarily loss of internet connectivity on client-side,
- the server is down or in an error state,
- the server is too busy handling other requests (resulting in request timeout)
Having a built-in resiliency mechanism is a good idea: the client application should automatically attempt to send the request again, hoping it will succeed, without requiring any input from user.
Implementation
How can this be achieve in Angular you might be wondering: enter HTTP interceptor and RXJS error handling operators.
The main idea is to:
- use an HTTP Interceptor to tap into the outgoing HTTP requests and
- use RXJS operators to detect if the request has failed and perform the automatic retry, as per our needs and configuration.
Implementation breakdown
- There is a default retry configuration, but usually is best to make this configurable. In our case, we use the values provided by the custom
AppConfigService
to overwrite the default ones (lines 15–33 in the above). - We intercept each HTTP request and pass it to the
retryPipe
to be processed. - The
retryPipe
uses theretryWhen
operator to process anyerrors
: for each failed request we check if we have exhausted the number of retry attempts or if the reason for the failure is a client-side error (HTTP status code ≤ 500) in which case we give up and throw the error, otherwise we send the request again (with an increased delay before the new retry).
Pros
- a better user experience, as the user is oblivious of the fact that an error has occurred,
- automatically retry all the requests that fail,
- retry options such as number of retry attempts and delay between each retry can be set as per application need,
- total control on which errors to retry, in the implementation above we are retrying only Server Side errors (i.e. responses with HTTP Status Code of 500 or above),
- retry mechanism is built with out-of-the-box components provided by Angular and RxJS, providing a good level of guarantee that they’ve been thoroughly tested
Cons
- the interceptor mechanism comes with a little overhead regarding performance, since all the HTTP request will have to go through it, and this could be an issue for certain applications — something to be mindful about,
- automatically retry all the requests that fail: this could be a disadvantage in certain scenarios, though you have the freedom to fine-tune the mechanism and define a blacklist to exclude certain requests from being retried.
Optional enhancement
- custom behaviour for specific scenarios,
- skip retry: you can easily update the
RetryHttpErrorsInterceptor
andretryPipe
to skip the retry for specific scenarios (e.g. based on the requests URL, responses status code etc), - extend
RetryRequestOptions
to include further parameters, as per your applications needs
Bonus: you can also find the full suite of unit tests for this implementation here