How to Prevent Duplicate API Requests in Axios
Duplicate API requests often lead to degraded application
performance, unnecessary server load, and unpredictable race conditions
in front-end applications. This guide covers the most effective
strategies for preventing duplicate HTTP calls in Axios, ranging from
native cancellation using AbortController and custom
promise-based request deduplication to rate-limiting techniques and
global interceptor solutions.
1. Canceling Previous Requests with AbortController
In modern Axios (v0.22.0+), the standard Web API
AbortController is the recommended way to cancel redundant
in-flight requests. This approach is ideal for search bars,
auto-complete components, or tab navigation where only the most recent
user action matters.
import axios from 'axios';
let controller = null;
async function fetchData(query) {
// Cancel previous request if it is still pending
if (controller) {
controller.abort();
}
// Create a new AbortController instance
controller = new AbortController();
try {
const response = await axios.get('/api/search', {
params: { q: query },
signal: controller.signal
});
return response.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled:', error.message);
} else {
throw error;
}
} finally {
controller = null;
}
}2. In-Flight Promise Caching (Request Deduplication)
When multiple components independently request the exact same data simultaneously, you can share a single network request among all callers by caching the pending promise.
import axios from 'axios';
const pendingRequests = new Map();
function generateRequestKey(config) {
const { method, url, params, data } = config;
return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&');
}
export function deduplicatedRequest(config) {
const key = generateRequestKey(config);
if (pendingRequests.has(key)) {
return pendingRequests.get(key);
}
const promise = axios(config)
.then((response) => {
pendingRequests.delete(key);
return response;
})
.catch((error) => {
pendingRequests.delete(key);
throw error;
});
pendingRequests.set(key, promise);
return promise;
}3. Global Request Cancellation via Interceptors
For applications requiring automated deduplication across all endpoints, Axios interceptors can centrally track and cancel duplicate requests before new ones are dispatched.
import axios from 'axios';
const pendingMap = new Map();
function getUrlKey(config) {
return `${config.method}_${config.url}_${JSON.stringify(config.params)}_${JSON.stringify(config.data)}`;
}
const apiClient = axios.create();
apiClient.interceptors.request.use((config) => {
const key = getUrlKey(config);
// If duplicate request exists, cancel it
if (pendingMap.has(key)) {
const controller = pendingMap.get(key);
controller.abort();
pendingMap.delete(key);
}
const controller = new AbortController();
config.signal = controller.signal;
pendingMap.set(key, controller);
return config;
});
apiClient.interceptors.response.use(
(response) => {
const key = getUrlKey(response.config);
pendingMap.delete(key);
return response;
},
(error) => {
if (error.config) {
const key = getUrlKey(error.config);
pendingMap.delete(key);
}
return Promise.reject(error);
}
);4. Debouncing and Throttling
When duplicate requests stem from rapid user interactions (such as repeated button clicks, typing, or scrolling), apply debouncing or throttling to control invocation frequency before invoking Axios.
- Debouncing: Delays execution until a specified period of inactivity has elapsed. Best suited for search inputs and auto-save features.
- Throttling: Enforces a maximum execution rate over time. Best suited for infinite scroll listeners and resize handlers.
Using utility libraries like Lodash:
import debounce from 'lodash/debounce';
const handleSearch = debounce(async (searchTerm) => {
const response = await axios.get(`/api/items?search=${searchTerm}`);
console.log(response.data);
}, 300);5. UI State Locking
A fundamental, non-network approach to avoiding duplicate POST, PUT, or DELETE mutations is managing UI state. Disabling submit buttons or displaying loading spinners immediately upon form submission prevents rapid multiple clicks from triggering identical payloads.