Axios Async/Await with Retry
Tags: axios, javascript, tutorial, async
Published: 2021-03-09 04:15:58

If you have been coding javascript for a while, you'd probably have known about [Axios](https://www.npmjs.com/package/axios). It is a famous JS library for making HTTP request. Whether you are back-end or front-end developer, Axios is essential to access API services out there.

Alas, reliability is scarce even when we're in 2020s. Things happen, and your HTTP request might get stalled. So what do we do? In some cases, we will try again until we get the data we need. In those cases, equipping Axios with retry capability is necessary.

In this post, I'm going to show how to equip Axios with retry capability i.e resend HTTP request when server doesn't answer. First, the native way. Then, we're going to use some libraries.

I feel the need to write this tutorial since most examples of Axios are written in `.then` pattern. I'm accustomed to `async/await` pattern since it feels more familiar (especially if you learn other OOP languages). Performance-wise, [both are internally equal](https://stackoverflow.com/questions/54495711/async-await-vs-then-which-is-the-best-for-performance).

Anyway, let's dive in to our code. I'm going to make a Node.JS project. Feel free to use whatever javascript stuff to suit your needs. Also, make sure your dependencies are fulfilled. I already installed Node.JS and NPM for this tutorial.

First, make new project using Terminal. 
```bash
$ mkdir request-retry
$ cd request-retry
$ npm init -y
$ touch index.js
```


Then, install axios package.
```bash
$ npm install axios
```

Now, we're going to edit `index.js` using code editor. I'm going to make HTTP request to https://mock.codes and see if it responds.
```javascript
const axios = require('axios')

const myRequest = async () => {
  try {
    const myConfig = {
      headers: {
        Authorization: 'Basic lorem12345'
      }
    }
    
    const req = await axios.get('https://mock.codes/200', myConfig);
    console.log(req.data);
  } catch (error) {
    console.log(error.response.data);
  }
}

myRequest();
```

Now, run `index.js`
```bash
$ node index.js
```
and we will get this result
> _{ statusCode: 200, description: 'OK' }_

Things are okay, right? Now, I'm going to rig this HTTP request by setting an unreasonably low timeout. To do that, add `timeout` in the request config. Let's check `index.js` again and edit `myConfig` so it looks like this.
```javascript
    const myConfig = {
      headers: {
        Authorization: 'Basic lorem12345'
      },
      timeout: 10 // 10ms timeout so servers hate you
    }
```

If I run `$ node index.js` again, I'll probably get something like this.
> _UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined_

What happened? Due to unfinished request, `req` doesn't get data from server. Therefore, its value is `undefined`. We can't get keys from `undefined`, hence the error. 

It is time to implement Axios with retry capability. But before going any further, I want to make clear when referring to `retry`, mostly we want to have control over two things:
* How many times we want to retry
* how long we want to wait for each trial

There are two main ways to do this:
1. Implement directly
2. Use package

### Direct Implementation
This option means doing everything from scratch. It's not too difficult, though. It is suitable option when we just need few types of request in our code and adding package would burden the app.

One simple approach is enveloping every request with loop. Now let's say I'm willing to retry 3 times and 50 miliseconds for each request. This is the example of working code.
```javascript
const axios = require('axios');

const myRequest = async () => {
  try {
    const retries = 3 // amount of retries we're willing to do
    const myConfig = {
      headers: {
        Authorization: 'Basic lorem12345'
      },
      // we're willing to wait 50ms, servers still hate you
      timeout: 50 
    }
    for (var i=0; i<retries; i++) {
      try {
        const req = await axios.get('https://mock.codes/200', myConfig);
        if (req) {
          console.log(req.data);
          break;
        } else {
          console.log('cannot fetch data');
        }
      } catch (error) {
        console.log('cannot fetch data');
      }
    }
  } catch (e) {
    console.log(e);
  }

  myRequest();
}
```
It's quite long, but if we're not doing it often across one project, this solution fits nicely.

### Use Package
There are times when we have to connect to many endpoints with different characteristics. In such circumstance, using package is justified. 

There are 3 famous packages that satisfy our needs:
1. [retry](https://www.npmjs.com/package/retry), a general purpose retry operation.
2. [axios-retry](https://www.npmjs.com/package/axios-retry), most popular retry add-on for Axios
3. [retry-axios](https://www.npmjs.com/package/retry-axios), second most popular retry add-on for Axios

I will use `retry-axios` since it provides easier implementation on `async/await` pattern. Now don't forget to read on its [documentation](https://github.com/JustinBeckwith/retry-axios#readme). Also, don't forget to install it using Terminal.
```bash
$ npm install retry-axios
```

This is an example of working code.
```javascript
const rax = require('retry-axios');
const axios = require('axios');

rax.attach();
const myRequest = async () => {
  try {
    const myConfig = {
      raxConfig: {
        retry: 5, // number of retry when facing 4xx or 5xx
        noResponseRetries: 5, // number of retry when facing connection error
        onRetryAttempt: err => {
          const cfg = rax.getConfig(err);
          console.log(`Retry attempt #${cfg.currentRetryAttempt}`); // track current trial
        }
      },
      timeout: 50 // don't forget this one
    }
    const req = await axios.get('https://mock.codes/200', myConfig);
    console.log(req.data);
  } catch (error) {
    console.log(error);
  }
}

myRequest();
```

Instead of doing loop, I basically attach `retry-axios` object to Axios. My part is handling config for the request. It is much simpler and delivers same result as direct implementation.

That's it! Now we can make Axios more reliable in sending HTTP request.

Do you find it useful, informative, or insightful ?
Do you find mistakes in my tutorial ?
Do you have any questions?
Feel free to comment below 👇 and let me know.