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.

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.