Code Monkey home page Code Monkey logo

fetch-retry's Introduction

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);
    });

fetch-retry's People

Contributors

afmelsaidy avatar alecmev avatar bamse16 avatar christophehurpeau avatar dependabot[bot] avatar dominique-blockchain avatar dwiyatci avatar fzembow avatar jayvolr avatar jcchavezs avatar jonbern avatar knottautodesk avatar mattccrampton avatar ptolemybarnes avatar pwambach avatar robinpokorny avatar solution avatar thomsbg avatar vishalvijay avatar x3cion avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

fetch-retry's Issues

I want to update fetch-retry after v3 but it didn't work

Thanks for the great fetch-retry! I use it from v2.0.0
I do it using fetch-retry like this.

fetchRetry('https://xxxx', {
      mode: "cors",
      method: "POST",
      headers: {
         "Content-Type": "application/json;charset=utf-8",
         Accept: "application/json",
      },
      retries: 3,
      retryDelay: 800,
      body: 'xxxxxxxx',
    }),

It works fine up to ver:2.2.3, but ver:3.x.x or later can't communicate normally.
Communication is no response and there is no error,
and it is not possible to update after ver3.
What should I do, has anything changed since ver3?

Thank you very much!

Type error with node-fetch

Code:

import fetchRetry from 'fetch-retry';
import originalFetch from 'node-fetch';

const fetch = fetchRetry(originalFetch);

TypeScript error:

TSError: ⨯ Unable to compile TypeScript:
test.ts:4:26 - error TS2345: Argument of type '(url: RequestInfo, init?: RequestInit | undefined) => Promise<Response>' is not assignable to parameter of type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
  Types of parameters 'url' and 'input' are incompatible.
    Type 'RequestInfo' is not assignable to type 'import("/Users/suzil/Documents/ownup/test/node_modules/node-fetch/@types/index").RequestInfo'.
      Type 'Request' is not assignable to type 'RequestInfo'.
        Type 'Request' is missing the following properties from type 'Request': size, buffer

4 const fetch = fetchRetry(originalFetch);

Using latest versions of both libraries:

"dependencies": {
  "fetch-retry": "^5.0.1",
  "node-fetch": "^3.0.0"
}

Repo here: https://github.com/suzil/fetch-retry-type-error

Do you really need isomorphic-fetch in here?

Useful tool!

But we don't need isomorphic-fetch, since we already polyfill with another approach and do not need node support... isomorphic-fetch adds unnecessary bloat to the bundled javascrit.

Do you feel strongly about having it as a dependency here? Feels like users could provide their own polyfill in a wrapper if needed.

Using fetch-retry with fetchMock does not work

Hi @jonbern ,

I've started using fetch-retry in my React application, it works fine until I run the tests. We're using fetch-mock to mock fetch calls to the server, and it appears that fetch is no longer mocked. According to the troubleshooting section of the fetch-mock documentation, this can happen if fetch is imported/required in a local variable, rather than using the global variable.

After checking the source code of fetch-retry, I see that this is indeed the case:

var fetch = require('isomorphic-fetch');

Since isomorphic-fetch adds fetch as a global, it appears that it is not necessary to import fetch in a local variable in fetch-retry. That would also make fetch-retry work seamlessly with fetch-mock (and I suspect other mocking libraries too). Similarly, we could use the Promise global directly according to the es6-promise documentation.

Would you be open to a pull request for that?

Cheers,
Matthieu

Browser usage

Please show us a working example for how you use fetch-retry in a browser.

Access current URL in custom retryOn

Thanks for the great fetch-retry!

Is there a way to access the current URL in custom retryOn? Right now, I'm using response.url but this does not work on exceptions. Any suggestions?

upgrade guide to v6

Not following the changes in the new v6. Any chance an upgrade guide can be provided to help smooth the transition?

Make defaults configurable

There are cases where you want to plug this library as a replacement for fetch but transparently. In such cases one can't take advantages of the configurable features like retries, retryDelay or retryOn because original fetch signature does not accept such parameters. Having the possibility to pass those values on require will allow users to plug this fetch like any other fetch.

Related to openzipkin/zipkin-js#485

Ping @jonbern @petermetz

Suggestion: Add an onRetry callback for reporting/logging

First of all I want to thank you for providing this wrapper, great work!

I have a small suggestion that I think could be useful for multiple users of this package.

I wanted a way to send metrics / logs when ever a retry is scheduled to happen, but could not find a good place to inject that. Right now I have modified the retryDelay function to emit the logs as a workaround, but feels a bit hacky.

I suggest adding a optional setting called onRetry which could be a function with the following signature

function(attempt, maxRetries, delay, error, response)

The function should be called (if given) right before the setTimeout call in retry function https://github.com/jonbern/fetch-retry/blob/master/index.js#L126

I can help with the implementation if you want to, just let me know what you think about the idea before i start.

Change url?

Hi how can i change/update url after fail?

for example:

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}`);

       /////////////////////
       // get new url from server and retry
       url = new_url();      
      ///////////////////////

        return true;
      }
    })
    .then(function(response) {
      return response.json();
    }).then(function(json) {
      // do something with the result
      console.log(json);
    });

Set up Fetch-retry

Hi, I've tried to use fetch-retry but I don't get it.
I've read the doc, I change my code but how am I supposed to link the API to my code ?
I'm not a pro, it might be a stupid question, but it could be good to add it to the README.

Thanks !

onRetry doesn't work with async function

Use case is to get the response body json in the onRetry function;

retryOn: async (attempt: number, error: Error, response: Response) => false

will retry forever because it returns a promise and if (Promise) is true

so I can't do something like:

retryOn: async (attempt: number, error: Error, response: Response) => {
    return await response.json().property === true
}

Retry on AbortController.abort()

We use a custom timeout to abort a long request using signal with AboutСontroller.abort(), so as not to make the user wait for a long time and make another attempt

After calling AbortController.abort(), fetch-retry stops requesting our server, but retryOn and retryDelay are called .

How do I make a fetch-retry to make a request to the server again for certain conditions after calling AbortController.abort()?

NPM install fails

$ npm install fetch-retry --save

> [email protected] postinstall /home/.../code/ride-hailing/app/node_modules/pngquant-bin
> node lib/install.js

  ⚠ The `/home/.../code/ride-hailing/app/node_modules/pngquant-bin/vendor/pngquant` binary doesn't seem to work correctly
  ⚠ pngquant pre-build test failed
  ℹ compiling from source
  ✔ pngquant pre-build test passed successfully
  ✖ Error: pngquant failed to build, make sure that libpng-dev is installed
    at Promise.all.then.arr (/home/.../code/ride-hailing/app/node_modules/pngquant-bin/node_modules/bin-build/node_modules/execa/index.js:231:11)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:182:7)
npm WARN [email protected] requires a peer of react-dom@^15.4.2 but none is installed. You must install peer dependencies yourself.
npm WARN [email protected] requires a peer of stylelint@^8.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: `node lib/install.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/.../.npm/_logs/2018-10-08T10_33_35_194Z-debug.log
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.1 LTS
Release:        18.04
Codename:       bionic
{ 'ride-hailing-app': '1.0.0',
  npm: '6.4.0',
  ares: '1.13.0',
  cldr: '33.0',
  http_parser: '2.8.0',
  icu: '61.1',
  modules: '59',
  napi: '3',
  nghttp2: '1.32.0',
  node: '9.11.2',
  openssl: '1.0.2o',
  tz: '2018c',
  unicode: '10.0',
  uv: '1.19.2',
  v8: '6.2.414.46-node.23',
  zlib: '1.2.11' }

Remove dependency with isomorphic-fetch

Remove dependency with isomorphic-fetch and allow any fetch package.

Few fetch libraries are listed below.

Leaving this option to the developer will allows them to use their preferred fetch library by avoiding unnecessary dependency.

Recommended API change.

var fetch = require('fetch-retry');

The above code has to be changed to

var originalFetch = require('isomorphic-unfetch');
var fetch = require('fetch-retry')(originalFetch);

Making the above change will break the existing API, so we'll have to make a major release.

Question about 'retryOn'?

I want to add retries to our fetch lib and this wrapper looks perfect. However, I am wondering if there is an issue w/the default settings. If you don't pass in retryOn yourself, internally it is set to an empty array:
https://github.com/jonbern/fetch-retry/blob/master/index.js#L28
and therefore the result always resolves on this line:
https://github.com/jonbern/fetch-retry/blob/master/index.js#L73.
Is that intentional? It seems like the original code was expecting the default value of retryOn to be undefined.

This line, including the one where the default of empty array is set, are 3 years old so I think I must be missing something. Otherwise, why is retryOn not a required input, since the rest of the retry settings will never be used?

Execute function on retry

Hello, thanks for this amazing library!

I am looking for a way to execute some code before a retry, is it possible?

Something like:


fetch(url, {
    retries: 3,
    retryDelay: 1000
    onRetry: () => {
        console.log('[INFO] Request failed, retrying...');
    }
})
  .then(function(response) {
    return response.json();
  });

Behaviour after retries are expired

Hi there - firstly, thanks very much for putting this awesome library out there - much appreciated!

I have a wee suggestion on the behaviour when the maximum number of retries is reached without a successful response. Currently, the promise is rejected:

reject(response);

However, the underlying fetch API does not behave this way, instead - it returns a response object with the appropriate status code. For fetch-retry to be a 'drop-in' replacement for the fetch API, I think it should behave in the same way, and resolve the promise with the most recent response, rather than rejecting it:

         resolve(response);

I'm interested to hear your thoughts and I'm happy to submit a PR if you like?

5.0.0 doesn't work in Node environment (e.g. with `node-fetch`)

👋🏼 Hi!

We noticed strange errors when attempting to upgrade 5.0.0, throughout our unit tests. Anywhere we invoked a fetch-retry-wrapped fetch, we got this error:

ReferenceError: Request is not defined

It traces to this PR: #59. Request isn't part of Node's global environment, so this check causes a ReferenceError. This applies regardless of fetch implementation, so I think it will unfortunately break all projects using Node.

I think a typeof Request !== 'undefined' prior to the instanceof check might be enough to fix things for Node. I would normally put up a PR but I'm swamped at the moment -- I can follow up on this when I have more time if that would be helpful.

Consider transpiling to ES5?

Would you consider accepting a PR which transpiles this code into ES5 compatible Javascript as part of the build process?

307 Redirect

In some cases an API endpoint can return a 307 redirect with a location header to another endpoint. Currently, fetch-retry will reattempt using the original endpoint instead of the new location. Is there a way to update the original request with the new endpoint during a subsequent retry?

This of course is enabled by default with fetch.
https://developer.mozilla.org/en-US/docs/Web/API/fetch

image

When function is provided to `retryOn`, retries value is not honored

In the example below, when passing in retries of 1, it defaults to that of 3. For some reason the retries is overwritten by the default value when a function is passed into retryOn.

fetch(url, {
    retries: 1,
    retryOn: function(attempt, error, response) {
      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);
    });

When just pass an array to retryOn, it honors my retries value.

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

Type definition broken with PR #33

The Typescript type definition is broken after merging PR #33

Please refer this line https://github.com/vishalvijay/fetch-retry/blob/5a2eb3c0e83cec2b62d5277e7082f0662a59c56a/index.d.ts#L20

It looks like, the current type is defined based on the argument to isomorphic-fetch. But looks like that is not the actual type.

Please refer the below-listed type definitions

isomorphic-fetch => https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/isomorphic-fetch/index.d.ts#L8
isomorphic-unfetch => https://github.com/developit/unfetch/blob/master/packages/isomorphic-unfetch/index.d.ts#L15
cross-fetch => https://github.com/lquixada/cross-fetch/blob/master/index.d.ts#L9

Based on the above findings we need to change the type definition of the argument of fetchBuilder function. And it's better to use the type of default fetch API so that it will be compatible with all the packages.

Also, the returning fetchRetry function's first argument's type suppose to be RequestInfo not String.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.