I have one API that time to time returns ECONNRESET
. So I'd like to do retry on such an error. Do you have plans to add retry
feature to this library?
any update on this?
I think this is very important. The scenario maybe when we implement Axios for mobile web, then the user switch the internet source from WiFi to any kind of mobile connection.
I don't think this kind of functionality should be in this library. If you need this, you can create a interceptor for this.
@jtangelder any ideas how should it look like?
function retryFailedRequest (err) {
if (err.status === 500 && err.config && !err.config.__isRetryRequest) {
err.config.__isRetryRequest = true;
return axios(err.config);
}
throw err;
}
axios.interceptors.response.use(undefined, retryFailedRequest);
Something like this! It retries 500 errors once.
I agree with @jtangelder that this doesn't belong in the core library. This is a great use case for interceptors as illustrated.
@jtangelder @mzabriskie thanks! actually it is great example for documentation too
+1 I wish this example was easier to find!
@jtangelder thanks a lot for the snippet
A little correction on the snippet, I think the returned error status should be 504 instead of 500.
@jtangelder very helpful example.
I'm wondering, any ideas on how to do something with exponential backoff? Is it possible to return a promise from retryFailedRequest
? Or other suggestions? Thanks!
The example in @jtangelder should be included in the cookbook page
You can now use axios-retry for this.
I agree. This should really be a part of the library, or at least better-documented.
@mericsson I am too in need of exponential backoff when retrying. I've put together the following which works great. You my want to add in checking for specific errors/status codes as this currently just intercepts and retries all errors.
axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) {
var config = err.config;
// If config does not exist or the retry option is not set, reject
if(!config || !config.retry) return Promise.reject(err);
// Set the variable for keeping track of the retry count
config.__retryCount = config.__retryCount || 0;
// Check if we've maxed out the total number of retries
if(config.__retryCount >= config.retry) {
// Reject with the error
return Promise.reject(err);
}
// Increase the retry count
config.__retryCount += 1;
// Create new promise to handle exponential backoff
var backoff = new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, config.retryDelay || 1);
});
// Return the promise in which recalls axios to retry the request
return backoff.then(function() {
return axios(config);
});
});
To use:
axios.get('/some/endpoint', { retry: 5, retryDelay: 1000 })
.then(function(res) {
console.log('success', res.data);
})
.catch(function(err) {
console.log('failed', err);
});
Config Options:
retry
- Number of times to retry the request after first failed request.
retryDelay
- Number of milliseconds to wait in between failed requests (defaults to 1).
I may make this a bit more configurable in a gist at some point.
@KyleRoss Thanks for the code, I made an adjustment for the retries to be exponential
// Create new promise to handle exponential backoff. formula (2^c - 1 / 2) * 1000(for mS to seconds)
const backOffDelay = config.retryDelay
? ( (1/2) * (Math.pow(2, config.__retryCount) - 1) ) * 1000
: 1;
const backoff = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, backOffDelay);
});
@KyleRoss Thanks for sharing.
The delay can be set increasingly:
const RETRY_TIMEOUTS = [1, 3, 5, 10]; // seconds
const delay = RETRY_TIMEOUTS[config.retryCount] * 1000;
If you want to retry an 4xx or 5xx request, you need work with the error object like that:
To intercept your requests:
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
To intercept your responses:
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
Awesome ideas. I rolled a fair bit of this up into an npm module, if folks are interested:
https://github.com/JustinBeckwith/retry-axios
Happy to take feature requests ✨
axios-retry
vs retry-axios
@KyleRoss I tried adding the retry and retryDelay configuration options to AxiosRequestConfig but it doesnt look like they exist. How did you get that to work?
@lawloretienne you need you make sure you add the code in my example prior to using any of the retry parameters. Since it acts more like a plugin, the new properties are not documented as part of axios itself.
config.__retryCount = config.__retryCount || 0;
@KyleRoss Unable to pass config config, each 'config.__reyCount' is undefined.
https://github.com/axios/axios/blob/v0.19.0/lib/core/mergeConfig.js
Custom properties are now filtered out.
@dennisreimann custom config are now filtered out in v0.19, and now we can pass config like this:
axios.defaults.headers.common['retry'] = 3
axios.defaults.headers.common['retryDelay'] = 1000
axios.defaults.headers.common['retryCount'] = 0
@mericsson I am too in need of exponential backoff when retrying. I've put together the following which works great. You my want to add in checking for specific errors/status codes as this currently just intercepts and retries all errors.
axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) { var config = err.config; // If config does not exist or the retry option is not set, reject if(!config || !config.retry) return Promise.reject(err); // Set the variable for keeping track of the retry count config.__retryCount = config.__retryCount || 0; // Check if we've maxed out the total number of retries if(config.__retryCount >= config.retry) { // Reject with the error return Promise.reject(err); } // Increase the retry count config.__retryCount += 1; // Create new promise to handle exponential backoff var backoff = new Promise(function(resolve) { setTimeout(function() { resolve(); }, config.retryDelay || 1); }); // Return the promise in which recalls axios to retry the request return backoff.then(function() { return axios(config); }); });
To use:
axios.get('/some/endpoint', { retry: 5, retryDelay: 1000 }) .then(function(res) { console.log('success', res.data); }) .catch(function(err) { console.log('failed', err); });
Config Options:
retry
- Number of times to retry the request after first failed request.
retryDelay
- Number of milliseconds to wait in between failed requests (defaults to 1).I may make this a bit more configurable in a gist at some point.
thanks for help!
Most helpful comment
@mericsson I am too in need of exponential backoff when retrying. I've put together the following which works great. You my want to add in checking for specific errors/status codes as this currently just intercepts and retries all errors.
To use:
Config Options:
retry
- Number of times to retry the request after first failed request.retryDelay
- Number of milliseconds to wait in between failed requests (defaults to 1).I may make this a bit more configurable in a gist at some point.