fetch-retry vs axios-retry vs got vs retry-axios vs superagent-retry
Implementing Robust HTTP Retry Logic in JavaScript Applications
fetch-retryaxios-retrygotretry-axiossuperagent-retrySimilar Packages:

Implementing Robust HTTP Retry Logic in JavaScript Applications

This comparison evaluates five npm packages designed to handle transient network failures by automatically retrying failed HTTP requests. axios-retry and retry-axios are plugins specifically for the Axios library, offering different levels of control and maintenance status. fetch-retry wraps the native fetch API to add retry capabilities without replacing the underlying client. got is a complete, human-friendly HTTP client for Node.js with built-in retry logic. superagent-retry extends the Superagent library, though both the plugin and its parent library face significant maintenance challenges. These tools help developers build resilient applications that can recover from temporary network glitches, 5xx server errors, or rate limiting without manual intervention.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
fetch-retry4,563,42531755.2 kB62 years agoMIT
axios-retry02,01633.6 kB582 years agoApache-2.0
got014,932371 kB0a month agoMIT
retry-axios050964.9 kB04 months agoApache-2.0
superagent-retry084-1010 years ago-

Implementing Robust HTTP Retry Logic: A Deep Dive into JavaScript Solutions

Network requests fail. It is not a matter of "if," but "when." Whether it is a flaky mobile connection, a temporary server overload (503), or a rate limit (429), professional applications must handle these hiccups gracefully. Relying on manual try...catch loops is error-prone and clutters your business logic.

This analysis compares five specific solutions for automating retries: axios-retry, fetch-retry, got, retry-axios, and superagent-retry. We will look at how they integrate, how they handle timing, and which ones you should actually put into production today.

πŸ—οΈ Integration Model: Plugins vs. Built-in vs. Wrappers

The first major difference is how these tools attach to your network layer. Some are plugins for existing clients, one is a built-in feature, and another is a wrapper around the native API.

axios-retry acts as an interceptor plugin. You install it alongside Axios and register it once. It hooks into Axios's internal promise chain.

// axios-retry setup
import axios from 'axios';
import axiosRetry from 'axios-retry';

const client = axios.create();
axiosRetry(client, { retries: 3 });

// Usage remains standard Axios
client.get('https://api.example.com/data');

retry-axios also uses the interceptor pattern but attaches to the global Axios instance or a specific one via a slightly different API structure. It modifies the request config before it goes out.

// retry-axios setup
import axios from 'axios';
import { attach } from 'retry-axios';

attach({ axiosInstance: axios });

// Usage remains standard Axios
axios.get('https://api.example.com/data');

fetch-retry is a wrapper function. It does not modify the global fetch; instead, it returns a new function that you use in place of the native one. This keeps the global scope clean.

// fetch-retry setup
import fetchRetry from 'fetch-retry';
const fetchWithRetry = fetchRetry(fetch);

// Usage replaces native fetch
fetchWithRetry('https://api.example.com/data', { retries: 3 })
  .then(res => res.json());

got requires no plugins. It is a complete HTTP client where retry logic is a core configuration option passed directly into the request.

// got setup (no plugin needed)
import got from 'got';

// Usage includes retry config directly
const response = await got('https://api.example.com/data', {
  retry: { limit: 3 }
});

superagent-retry extends the Superagent request prototype. You call .retry() explicitly on the request chain, similar to how you might call .timeout().

// superagent-retry setup
import request from 'superagent';
import requestRetry from 'superagent-retry';

requestRetry(request);

// Usage chains the retry method
request
  .get('https://api.example.com/data')
  .retry(3)
  .end((err, res) => { /* handle */ });

⏱️ Delay Strategies: Exponential Backoff vs. Fixed Intervals

When a request fails, hammering the server immediately often makes things worse. Good retry logic uses "backoff" β€” waiting longer between each attempt. Let's see how each package handles this.

axios-retry provides a built-in exponential backoff function but allows you to write your own. This is crucial for respecting rate limits.

// axios-retry: Custom delay calculation
axiosRetry(client, {
  retryDelay: (retryCount) => {
    return retryCount * 1000; // Linear delay: 1s, 2s, 3s
  }
});

retry-axios supports a similar configuration where you define the delay logic based on the attempt number.

// retry-axios: Custom delay
attach({
  axiosInstance: axios,
  retryConfig: {
    retryDelay: (count, error) => {
      return Math.pow(2, count) * 100; // Exponential
    }
  }
});

fetch-retry lets you pass a retryDelay function in the options object. It is straightforward and functional.

// fetch-retry: Custom delay
fetchWithRetry(url, {
  retryDelay: function(attempt) {
    return Math.pow(2, attempt) * 1000;
  }
});

got has a very sophisticated built-in backoff strategy by default (exponential with jitter). You can override it, but the defaults are often sufficient for production use.

// got: Overriding default backoff
got(url, {
  retry: {
    calculateDelay: ({ attemptCount }) => {
      return attemptCount * 500;
    }
  }
});

superagent-retry typically uses a fixed delay or a simple exponential strategy internally, with less flexibility for custom algorithms compared to the Axios plugins.

// superagent-retry: Fixed delay usage
request
  .get(url)
  .retry(3, 1000) // Retries 3 times with 1000ms delay
  .end(callback);

🎯 Filtering: Deciding When to Retry

Not every error deserves a retry. A 404 (Not Found) will never fix itself by trying again. You only want to retry on network errors, 5xx server issues, or 429 (Too Many Requests).

axios-retry exports a helper isNetworkOrIdempotentRequestError but also lets you write a boolean function to decide.

// axios-retry: Custom condition
axiosRetry(client, {
  retryCondition: (error) => {
    // Retry only on 500 errors or network issues
    return error.response?.status === 500 || error.code === 'ECONNRESET';
  }
});

retry-axios uses a similar retryCondition callback in its configuration object.

// retry-axios: Custom condition
attach({
  retryConfig: {
    retryCondition: (error) => {
      return error.config && error.config.method === 'get';
    }
  }
});

fetch-retry provides a retryOn function. Since fetch only rejects on network failure (not HTTP error statuses), you often need to check the response status inside this function.

// fetch-retry: Check response status
fetchWithRetry(url, {
  retryOn: async (attempt, error, response) => {
    if (attempt >= 3) return false;
    if (response && response.status === 503) return true;
    return false;
  }
});

got allows you to specify which HTTP status codes trigger a retry via the statusCodes array, alongside network errors.

// got: Specific status codes
got(url, {
  retry: {
    limit: 3,
    statusCodes: [408, 413, 429, 500, 502, 503, 504]
  }
});

superagent-retry generally retries on network errors and specific server errors by default, but customizing this logic deeply is more verbose than in the Axios ecosystem.

// superagent-retry: Basic usage
// Primarily relies on the count argument; 
// complex filtering often requires manual wrapping
request.get(url).retry(3).end(...);

⚠️ Maintenance Status and Deprecation Warnings

This is the most critical section for architectural decisions. Using unmaintained software introduces security risks and compatibility bugs.

retry-axios has historically suffered from long gaps between releases and issues left unanswered by maintainers. While it still works, the community has largely migrated to axios-retry, which is more actively updated and has broader adoption. Recommendation: Do not start new projects with retry-axios. Use axios-retry instead.

superagent-retry depends on Superagent, a library that has seen a massive decline in usage in favor of Axios and native Fetch. The plugin itself sees very little activity. Recommendation: Avoid for new projects. If you are stuck on Superagent, plan a migration to Axios or Fetch.

axios-retry, fetch-retry, and got are all actively maintained. They receive regular updates to support new Node.js versions, fix security vulnerabilities, and improve TypeScript definitions. These are the safe choices for 2024 and beyond.

🌐 Environment Compatibility: Browser vs. Node.js

Where your code runs dictates which tool you can use.

  • got is Node.js only. It relies on Node-specific streams and HTTP agents. It will not work in a browser bundle without heavy (and often broken) shimming.
  • axios-retry and retry-axios work wherever Axios works: both Browser and Node.js. This makes them perfect for isomorphic applications (code shared between server and client).
  • fetch-retry works in any environment that supports fetch. This includes all modern browsers and Node.js 18+. For older Node versions, you need a polyfill like node-fetch.
  • superagent-retry works in both, but again, the parent library is heavy for browsers compared to native fetch.
// Example: Isomorphic retry with axios-retry
// Works in React (Browser) and Next.js API routes (Node)
import axios from 'axios';
import axiosRetry from 'axios-retry';

const api = axios.create();
axiosRetry(api, { retries: 3 });
export default api;
// Example: Node-only high-performance script with got
// Will crash if imported in a browser bundle
import got from 'got';

async function scrapeData() {
  return await got('https://api.data.com', { retry: { limit: 5 } });
}

πŸ“Š Summary Comparison

Featureaxios-retryfetch-retrygotretry-axiossuperagent-retry
Base ClientAxiosNative FetchBuilt-inAxiosSuperagent
EnvironmentBrowser & NodeBrowser & Node (18+)Node OnlyBrowser & NodeBrowser & Node
Setup StylePlugin / InterceptorWrapper FunctionBuilt-in ConfigPlugin / InterceptorPrototype Extension
Custom Delaysβœ… Full Controlβœ… Full Controlβœ… Full Controlβœ… Full Control⚠️ Limited
Maintenance🟒 Active🟒 Active🟒 Active🟑 Low ActivityπŸ”΄ Legacy / Low Activity
Bundle SizeMedium (depends on Axios)TinyLarge (Node libs)Medium (depends on Axios)Large

πŸ’‘ Final Recommendation

Your choice should depend on your existing stack and target environment:

  1. For Modern Node.js Services: Use got. It is the most powerful, has the best developer experience, and requires no extra plugins. Its built-in retry logic is robust and battle-tested.
  2. For React/Frontend or Isomorphic Apps: Use axios-retry. Axios remains the standard for complex frontend HTTP needs (interceptors, cancellation, broad browser support), and this plugin integrates perfectly.
  3. For Lightweight Modern Stacks: Use fetch-retry. If you are done with Axios and want to use the native fetch API, this is the simplest way to add reliability without bloating your bundle.
  4. Avoid: Do not choose retry-axios or superagent-retry for new architecture. The risk of unmaintained dependencies outweighs any minor API preferences they might offer. Stick to the actively maintained leaders in the ecosystem.

How to Choose: fetch-retry vs axios-retry vs got vs retry-axios vs superagent-retry

  • fetch-retry:

    Choose fetch-retry if you prefer using the native fetch API and want to avoid adding a heavy HTTP client like Axios or Got. It is ideal for modern browsers and Node.js 18+ environments where you want lightweight retry logic that simply wraps the existing global fetch function without changing your request syntax significantly.

  • axios-retry:

    Choose axios-retry if your project already relies on Axios and you need a actively maintained, feature-rich plugin. It offers excellent control over retry conditions, custom delays, and works seamlessly in both Node.js and browser environments. It is the standard choice for modern Axios-based stacks requiring robust error recovery.

  • got:

    Choose got if you are building a Node.js-only application and want a powerful, all-in-one HTTP client. Unlike the others, it does not require a separate plugin for retries; the functionality is built-in and highly configurable. It is best for backend services, scripts, and tools where developer experience and comprehensive features are prioritized over bundle size.

  • retry-axios:

    Avoid choosing retry-axios for new projects. While it functions as an interceptor for Axios similar to axios-retry, it has historically suffered from long periods of inactivity and lack of maintenance compared to its competitor. Unless you are maintaining a legacy codebase that strictly depends on it, axios-retry is the safer, more reliable alternative.

  • superagent-retry:

    Avoid choosing superagent-retry for new projects. Both this plugin and its parent library, Superagent, are largely considered legacy in the modern JavaScript ecosystem. They lack the active development and community support found in Axios or native fetch solutions. Migrating to axios-retry or fetch-retry is recommended for better long-term stability.

README for fetch-retry

fetch-retry

Adds retry functionality to the Fetch API.

It wraps any fetch API package (eg: isomorphic-fetch, cross-fetch, isomorphic-unfetch, or Node.js native's fetch implementation) and retries requests that fail due to network issues. It can also be configured to retry requests on specific HTTP status codes.

Node.js CI

npm package

npm install fetch-retry --save

Example

fetch-retry is used the same way as fetch, but also accepts retries, retryDelay, and retryOn on the options object.

These properties are optional, and unless different defaults have been specified when requiring fetch-retry, these will default to 3 retries, with a 1000ms retry delay, and to only retry on network errors.

const originalFetch = require('isomorphic-fetch');
const fetch = require('fetch-retry')(originalFetch);

// fetch-retry can also wrap Node.js's native fetch API implementation:
const fetch = require('fetch-retry')(global.fetch);
fetch(url, {
    retries: 3,
    retryDelay: 1000
  })
  .then(function(response) {
    return response.json();
  })
  .then(function(json) {
    // do something with the result
    console.log(json);
  });

or passing your own defaults:

const originalFetch = require('isomorphic-fetch');
const fetch = require('fetch-retry')(originalFetch, {
    retries: 5,
    retryDelay: 800
  });

fetch-retry uses promises and requires you to polyfill the Promise API in order to support Internet Explorer.

Example: Exponential backoff

The default behavior of fetch-retry is to wait a fixed amount of time between attempts, but it is also possible to customize this by passing a function as the retryDelay option. The function is supplied three arguments: attempt (starting at 0), error (in case of a network error), and response. It must return a number indicating the delay.

fetch(url, {
    retryDelay: function(attempt, error, response) {
      return Math.pow(2, attempt) * 1000; // 1000, 2000, 4000
    }
  }).then(function(response) {
    return response.json();
  }).then(function(json) {
    // do something with the result
    console.log(json);
  });

Example: Retry on 503 (Service Unavailable)

The default behavior of fetch-retry is to only retry requests on network related issues, but it is also possible to configure it to retry on specific HTTP status codes. This is done by using the retryOn property, which expects an array of HTTP status codes.

fetch(url, {
    retryOn: [503]
  })
  .then(function(response) {
    return response.json();
  })
  .then(function(json) {
    // do something with the result
    console.log(json);
  });

Example: Retry custom behavior

The retryOn option may also be specified as a function, in which case it will be supplied three arguments: attempt (starting at 0), error (in case of a network error), and response. Return a truthy value from this function in order to trigger a retry, any falsy value will result in the call to fetch either resolving (in case the last attempt resulted in a response), or rejecting (in case the last attempt resulted in an error).

fetch(url, {
    retryOn: function(attempt, error, response) {
      // retry on any network error, or 4xx or 5xx status codes
      if (error !== null || response.status >= 400) {
        console.log(`retrying, attempt number ${attempt + 1}`);
        return true;
      }
    })
    .then(function(response) {
      return response.json();
    }).then(function(json) {
      // do something with the result
      console.log(json);
    });

Example: Retry custom behavior with async

The retryOn option may also be used with async and await for calling asyncronous functions:

fetch(url, {
    retryOn: async function(attempt, error, response) {
      if (attempt > 3) return false;

      if (error !== null) {
        var json = await response.json();
        if (json.property !== undefined) {
          return true;
        }
      }
    })
    .then(function(response) {
      return response.json();
    }).then(function(json) {
      // do something with the result
      console.log(json);
    });