Exponential Backoff in Axios Retry Logic
Exponential backoff is a standard error-handling strategy that progressively increases the wait time between consecutive retries for failed network requests. In Axios, implementing exponential backoff ensures that your application gracefully handles transient errors—such as network dropouts, rate limits (HTTP 429), or temporary server unavailability (HTTP 503)—without overwhelming the destination server. This guide covers how to incorporate exponential backoff into Axios using custom interceptors as well as pre-built community solutions.
The Mathematics of Exponential Backoff
The core concept relies on multiplying the delay duration exponentially based on the retry attempt number:
\[\text{Delay} = \text{Base Delay} \times 2^{\text{retryCount}} + \text{Jitter}\]
Adding "jitter" (a small, randomized amount of time) prevents the "thundering herd" problem, where multiple client instances retry failed requests at the exact same millisecond.
Method 1: Implementing via Native Axios Interceptors
You can build a lightweight exponential backoff mechanism directly within Axios using response interceptors and standard JavaScript promises without external dependencies.
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
});
// Helper function to create a promise-based delay with jitter
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
apiClient.interceptors.response.use(null, async (error) => {
const { config, response } = error;
// Initialize retry count tracking
config.__retryCount = config.__retryCount || 0;
const maxRetries = 3;
const baseDelay = 1000; // 1 second
// Determine if error is retryable (Network error, 429, or 5xx server errors)
const isRetryable =
!response || (response.status >= 500 && response.status <= 599) || response.status === 429;
if (isRetryable && config.__retryCount < maxRetries) {
config.__retryCount += 1;
// Calculate delay: baseDelay * 2^(retryCount - 1) + randomized jitter
const exponentialDelay = baseDelay * Math.pow(2, config.__retryCount - 1);
const jitter = Math.random() * 200; // 0-200ms randomized offset
const totalDelay = exponentialDelay + jitter;
await wait(totalDelay);
// Re-execute the request with the updated config
return apiClient(config);
}
return Promise.reject(error);
});
export default apiClient;Method 2: Using the
axios-retry Plugin
For a production-ready and configurable implementation, the
axios-retry package provides built-in exponential backoff
functionality.
Installation
npm install axios-retryConfiguration
import axios from 'axios';
import axiosRetry from 'axios-retry';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
});
axiosRetry(apiClient, {
retries: 4, // Number of retry attempts
retryDelay: (retryCount) => {
// Built-in exponential backoff generator with jitter
return axiosRetry.exponentialDelay(retryCount);
},
retryCondition: (error) => {
// Retry on network errors or 5xx idempotent requests
return (
axiosRetry.isNetworkOrIdempotentRequestError(error) ||
error.response?.status === 429
);
},
shouldResetTimeout: true, // Reset request timeout between attempts
});
export default apiClient;Best Practices for Axios Retry Logic
- Limit Retries to Idempotent Methods: By default,
only retry safe or idempotent HTTP methods (
GET,PUT,DELETE,HEAD,OPTIONS). Retrying non-idempotent methods likePOSTcan result in duplicate transactions unless your server supports idempotency keys. - Cap Maximum Delay: Ensure an upper boundary (e.g., maximum 30 seconds) so that requests do not hang indefinitely during severe outages.
- Respect
Retry-AfterHeaders: If a server returns an HTTP 429 or 503 status code with aRetry-Afterheader, use that explicit delay duration instead of the calculated exponential backoff value.