A tiny, dependency-free TS library for adding retry logic to your favorite fetch implementation.
This library is not intended for production use.
Using npm:
npm install @helmturner/fetch-again
Using yarn:
yarn add @helmturner/fetch-again
import fetchWithRetry from "@helmturner/fetch-again";
const response = await fetchWithRetry(options, "https://example.com", {
method: "POST",
body: JSON.stringify({ foo: "bar" }),
});
A fetch implementation to use. If not provided, the library will attempt to find a fetch implementation in the following order:
fetch
(global)window.fetch
(browser)global.fetch
(node)require("node-fetch")
(must be installed as a dependency)
The number of times to retry the request. Defaults to 3
.
The exponential factor to use. Defaults to 2
. The timeout between retries will grow by this factor between each retry, e.g. for a factor of 2
the timeout will double with each retry.
The minimum timeout between retries, in milliseconds. Defaults to 1000
.
The maximum timeout between retries, in milliseconds. Defaults to 10000
.
Whether to randomize the timeout between retries. Defaults to false
.
A callback to be called after each retry. The callback will be passed the result of the request (regardless of whether returned or thrown) and the number of retries remaining. Defaults to a no-op.
A function that determines whether to retry the request. The function will be passed the result of the request (regardless of whether it was returned or thrown) and the number of retries remaining.
By default, the following steps are taken to determine whether to retry:
- If the number of retries remaining is
0
, the request will not be retried. - If the result is an instance of
Error
ornull
, the request will be retried. - If the result has an
ok
property, the request will only be retried if the value ofok
is falsey - Otherwise, If the result has any property that is a number between
100
and599
inclusive, the request will be retried only if the value of that property is408
,413
,429
,500
,502
,503
or504
. - If none of the above conditions are met, an error will be thrown.
IT IS HIGHLY RECOMMENDED THAT YOU PROVIDE A CUSTOM retryOn
FUNCTION IF USING A FETCH IMPLEMENTATION OTHER THAN node-fetch
OR THE fetch
API
The arguments to pass to the fetch implementation. The arguments should be the same as the arguments you would pass to the fetch implementation you are using - TypeScript will type them according to the fetch function passed.
With default fetch
implementation:
import fetchWithRetry from "@helmturner/fetch-again";
const options = {
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 10000,
randomize: true,
onRetry: (result, count) => {
console.log(`Retrying request. ${count} retries remaining.`);
},
retryOn: (result, count) => {
if (count === 0) return false;
if (result instanceof Error) return true;
if (result.status === 429) return true;
return false;
},
};
const response = await fetchWithRetry(options, "https://example.com", {
method: "POST",
body: JSON.stringify({ foo: "bar" }),
});
With custom fetch
implementation:
import fetchWithRetry from "@helmturner/fetch-again";
import axios from "axios";
const options = {
fetchFn: axios,
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 10000,
randomize: true,
onRetry: (result, count) => {
console.log(`Retrying request. ${count} retries remaining.`);
},
retryOn: (result, count) => {
if (count === 0) return false;
if (error.response && typeof error.response.status === "number") {
return [408, 413, 429, 500, 502, 503, 504].includes(
error.response.status
);
}
return true;
},
};
const response = await fetchWithRetry(options, {
url: "https://example.com",
method: "get",
});
ISC License (ISC) Copyright (c) 2023 Helm Turner