DEV Community

Yogi Saputro
Yogi Saputro

Posted on

Axios Async/Await with Retry

If you have been coding javascript for a while, you'd probably have known about 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.

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.

$ mkdir request-retry
$ cd request-retry
$ npm init -y
$ touch index.js
Enter fullscreen mode Exit fullscreen mode

Then, install axios package.

$ npm install axios
Enter fullscreen mode Exit fullscreen mode

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.

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();
Enter fullscreen mode Exit fullscreen mode

Now, run index.js

$ node index.js
Enter fullscreen mode Exit fullscreen mode

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.

    const myConfig = {
      headers: {
        Authorization: 'Basic lorem12345'
      },
      timeout: 10 // 10ms timeout so servers hate you
    }
Enter fullscreen mode Exit fullscreen mode

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.

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();
}
Enter fullscreen mode Exit fullscreen mode

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, a general purpose retry operation.
  2. axios-retry, most popular retry add-on for Axios
  3. 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. Also, don't forget to install it using Terminal.

$ npm install retry-axios
Enter fullscreen mode Exit fullscreen mode

This is an example of working code.

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();
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (7)

Collapse
 
bikamdhakal profile image
bikamdhakal

How to implement such functionality on post request
For example: User hits a login button it will send post request but during hitting that button the internet is low or off, after certain time if the internet is on that api needed to called automatically when the internet gets on.

Collapse
 
yogski profile image
Yogi Saputro

You can wrap the POST request as usual, then adjust the timeout accordingly. If the network is unstable, set timeout to 3 seconds (3000 in code) and set retry to 3-5 times. If login is successful, user will be redirected immediately. If it failed, throw notification that login failed due to network issue, and user can try to log in again.

Be warned that it has trade-off of higher traffic for server.
Find the best configuration between timeout and retries that suit your needs.

Collapse
 
ayelen3976 profile image
Ayelen

Hi, If I dont want use the timeout , I just do the retries when my api have a error status 400 or 500. it can work?. because dont work me

Collapse
 
yogski profile image
Yogi Saputro

Hi,
It's okay to not use timeout.
In this case, if you receive 400 or 500 status, it will automatically retry the request.
However, please note that those response are considered error, so it will be catched.

To handle 400 or 500 status, put your logic after catch(error)

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 400 or 500
        onRetryAttempt: err => {
          const cfg = rax.getConfig(err);
          console.log(`Retry attempt #${cfg.currentRetryAttempt}`); // track current trial
        }
      },
    }
    const req = await axios.get('https://mock.codes/500', myConfig);
    console.log(req.data);
  } catch (error) {
    // 400 or 500 response will be handled here
    console.log(error.response.status);
    // continue your code here ...
  }
}

myRequest();
Enter fullscreen mode Exit fullscreen mode

Hope this helps.

Collapse
 
neerajkumargupta profile image
neerajkumargupta

Hi, this is not working for me. I tried and it only prints for first retry and after that comes out of the condition

Collapse
 
yogski profile image
Yogi Saputro

Hi, thanks for visiting.
Could you share your code and API you are trying to hit, please? This post primarily deal with retry mechanism. In your case, I guess it is about handling the response.

In this case, response of 400 and 500 will cause it to try again. You could check the response, if the response is 200, then there's no need to retry. Your request is success.

However, some API give 200 status for failed request. So you need to handle it manually.

Collapse
 
epravednikov profile image
Evgen

How pass headers to request with rax? not global