Retry Failed Requests with RxJS and the HttpClient in Angular

Share this video with your friends

Send Tweet
Published 5 years ago
Updated 4 years ago

When the communication with the backend fails, before giving the user a negative response, it might be worth trying to automatically recover from the error. In some cases, that can be as simple as retrying to issue the request again. Since the Angular HttpClient heavily relies on RxJS we can apply some of its operators to make this happen, such as retry, retryWhen and delay.

So here we have a simple app component, which internally gets people service injected. Here we have a fetch people function, which gets triggered whenever someone clicks that button here which then invokes on the people service that fetch people function. Internally here, we can see that an HTTP call gets made with that HTTP client, which comes from the Angular common HTTP package.

As you can see here, I on purpose commanded out here a working URL and replaced it with one which fails. So whenever I click the button, we can see we get a 404 status code and some error message gets printed out here.

In the ideal case, we would be able to somehow recover automatically from such a failure before even showing something to the user. What we could do for instance is to retry that call a couple of times. Luckily the HTTP client heavily relies on RXJS. So I can here import the RXJS operator and then use it here below and say retry for three times in total before you then show the error to the user.

If we now click the button, we can see that the call gets executed on the server, we get an error, and then here three further calls get made trying to recover now automatically from that failure. In a real world scenario, this might not be the best optimal way, because we would immediately invoke the server URL again. The chance that the server has recovered in the meantime is quite low.

So what we could try instead is to simply introduce a delay, so only retry it after you've maybe an incrementing amount of time. Instead of the retry operator, we could use the retry when operator which gives us here a parameter. Then here we can specify the number of retries, which let's say is three, and then return here the observable to not interrupt the chain.

We introduce here a delay, let's say one second just to make it more visible. Then we again get here flat map and say whenever that retries when its minus is bigger than zero, we return the observable of that value. So we simply continue basically that chain, the repetitions. We're never below our retries. So when retries are finished, we say observable.throw of that value, because we want to throw the error and interrupt that repetition chain.

Now we need to add some imports here for RXJS. Now, if we click that button, we can see that the requests are made, but there's a delay in between. Finally, basically the error again gets shown, because we didn't recover from that error on the server...

Alplob
Alplob
~ 5 years ago

Hi Juri, thanks a lot for this course, I really appreciated it. So far I have just one suggestion: I think it would worth to explain how to implement properly the operator Timeout of rxjs with this.

Juri Strumpflohner
Juri Strumpflohnerinstructor
~ 5 years ago

Hey @alplob. Thx for your suggestion. Could you however explain in more detail what you'd like to see related to "timeout"? You mean at the Angular HTTP level?

Alplob
Alplob
~ 5 years ago

Yes, I mean at the Angular HTTP level. I am developing a mobile app with Ionic and in some (bad) situations the response to the request can be very long, that is why I use Timeout with http calls that may impact the end user experience. Just a remark when the timeout is raised by rxjs, the resulting err is an "instanceof Error" but there is no err.error.message, just err.name (this refers to the previous video of this course).

Roland Pangu
Roland Pangu
~ 4 years ago

If you're running the latest version of rxjs, this will not work. You will have to have it like this: `
this.people$ = this.peopleService .fetchPeople() .pipe( retryWhen(error => { let retries = 3;

     return error.pipe(
       delay(1500),
       flatMap(err => {
         if (retries-- > 0) {
           return of(err);
         }
       })
     );
   })
 )`

And you will also have to import the operators differently: import { delay, retryWhen, flatMap } from 'rxjs/operators'; import { of } from 'rxjs';

Juri Strumpflohner
Juri Strumpflohnerinstructor
~ 4 years ago

@Roland you're right. I already scheduled this course for being updated. Just finished the just bits of some new releases & then I'll update this one 👍