How to Retry Axios Requests by Error Code
Handling transient network failures and rate limits is essential for
building resilient applications. This article explains how to implement
custom retry logic in the Axios HTTP client based on specific HTTP
status codes and network error codes, covering both the popular
axios-retry plugin and a vanilla Axios interceptor
approach.
Method 1: Using the
axios-retry Plugin
The simplest and most robust way to add retry functionality is using
the axios-retry library. It provides a built-in
retryCondition hook to inspect errors and decide whether to
retry.
1. Installation
npm install axios axios-retry2. Implementation
const axios = require('axios');
const axiosRetry = require('axios-retry');
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
});
// Define specific status codes to retry
const RETRYABLE_STATUS_CODES = [429, 502, 503, 504];
// Define specific network error codes to retry
const RETRYABLE_NETWORK_ERRORS = ['ECONNABORTED', 'ETIMEDOUT', 'ECONNRESET'];
axiosRetry(apiClient, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
// 1. Check HTTP response status codes
if (error.response && RETRYABLE_STATUS_CODES.includes(error.response.status)) {
return true;
}
// 2. Check Node.js / network error codes
if (error.code && RETRYABLE_NETWORK_ERRORS.includes(error.code)) {
return true;
}
// Do not retry on client errors like 400, 401, 403, 404
return false;
},
});
module.exports = apiClient;Method 2: Native Axios Response Interceptors
If you prefer not to add external dependencies, you can implement retry logic directly inside an Axios response interceptor by tracking retry counts in the request configuration.
const axios = require('axios');
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
});
const RETRYABLE_STATUS_CODES = [429, 502, 503, 504];
const MAX_RETRIES = 3;
const BASE_DELAY_MS = 1000;
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const config = error.config;
// Avoid retrying if no config exists
if (!config) {
return Promise.reject(error);
}
// Initialize retry state
config.__retryCount = config.__retryCount || 0;
// Define custom condition
const isRetryableStatus = error.response && RETRYABLE_STATUS_CODES.includes(error.response.status);
const isTimeout = error.code === 'ECONNABORTED';
const shouldRetry = (isRetryableStatus || isTimeout) && config.__retryCount < MAX_RETRIES;
if (shouldRetry) {
config.__retryCount += 1;
// Calculate exponential backoff delay
const delay = BASE_DELAY_MS * Math.pow(2, config.__retryCount - 1);
await new Promise((resolve) => setTimeout(resolve, delay));
// Re-send the request with the updated config
return apiClient(config);
}
return Promise.reject(error);
}
);
module.exports = apiClient;Best Practices for Axios Retries
- Filter by HTTP Method: Avoid retrying
non-idempotent methods (like
POSTorPATCH) unless the API is explicitly designed to handle duplicate submissions safely. - Implement Backoff and Jitter: Use exponential backoff to avoid overwhelming recovering servers. Add randomized jitter if handling high concurrency.
- Respect
Retry-AfterHeaders: When handling429 Too Many Requests, checkerror.response.headers['retry-after']and delay the next attempt by the duration specified by the server.