'Axios: how to cancel request inside request interceptor properly?

I want to cancel the request if there's no token so I do like this:

instance.interceptors.request.use(config => {
  if (!getToken()) {
    console.log("interceptors: no access token");
  } else {
    config.headers.Authorization = "Bearer " + getToken().accessToken;
    return config;
  }
});

But in negative scenario there's an error TypeError: Cannot read property 'cancelToken' of undefined.



Solution 1:[1]

You cannot use the token inside the interceptors but instead throw Cancel

axios.interceptors.response.use(function (response) {
  throw new axios.Cancel('Operation canceled by the user.');
}, function (error) {
  return Promise.reject(error);
});

Refer to this post: https://github.com/axios/axios/issues/583

Solution 2:[2]

For later googlers, this is a good solution taken from the axios issue on github

instance.interceptors.request.use(config => {
  /* some logic */
  return {
    ...config,
    cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
  };
});

This is pretty much what Vijay proposed but in a more concise form.

Solution 3:[3]

@Kirill Taletski's answer solve this perfectly, but add one line:

const CancelToken = Axios.CancelToken;

then ,it gonna be like this :

instance.interceptors.request.use(config => {
  /* some logic */
  const CancelToken = Axios.CancelToken;
  return {
    ...config,
    cancelToken: new CancelToken((cancel) => cancel('Cancel repeated request'))
  };
});

Solution 4:[4]

here is the solution

import axios from 'axios';

const CancelToken = axios.CancelToken;
let cancel;

axios.interceptors.request.use((config) => {

  if (cancel) {
    cancel(); // cancel request
  }

  config.cancelToken =  new CancelToken(function executor(c)
    {
      cancel = c;
    })

  return config

}, function (error) {
  return Promise.reject(error)
});

Solution 5:[5]

I have implemented this in this way. I am not sure if this is the best solution, but for my use case is useful. My idea is not to cancel the last request. I would like to cancel previous requests to the same endpoint, and let the last one to do his job. For that reason I keep track of the request that are being executed.

// I keep track of the current requests that are being executed
const currentExecutingRequests = {};

axios.interceptors.request.use(
    (req) => {
        let originalRequest = req;

        if (currentExecutingRequests[req.url]) {
            const source = currentExecutingRequests[req.url];
            delete currentExecutingRequests[req.url];
            source.cancel();
        }

        const CancelToken = axios.CancelToken;
        const source = CancelToken.source();
        originalRequest.cancelToken = source.token;
        currentExecutingRequests[req.url] = source;

        // here you could add the authorization header to the request

        return originalRequest;
    },
    (err) => {
        return Promise.reject(err);
    }
);

axios.interceptors.response.use(
    (response) => {
        if (currentExecutingRequests[response.request.responseURL]) {
            // here you clean the request
            delete currentExecutingRequests[response.request.responseURL];
        }
        return response;
    },
    (error) => {
        const { config, response } = error;
        const originalRequest = config;

        if (axios.isCancel(error)) {
            // here you check if this is a cancelled request to drop it silently (without error)
            return new Promise(() => {});
        }

        if (currentExecutingRequests[originalRequest.url]) {
            // here you clean the request
            delete currentExecutingRequests[originalRequest.url];
        }

        // here you could check expired token and refresh it if necessary

        return Promise.reject(error);
    }
);

Solution 6:[6]

As of Axios v0.22.0 an AbortSignal is the recommended way to cancel from a request interceptor.

axios.interceptors.request.use(
  (requestConfig) => {
      /* some logic */
      return {
        ...requestConfig,
        signal: AbortSignal.abort()
      };
    }
  },
  (error) => {
    return Promise.reject(error);
  }
);

Solution 7:[7]

So for whatever reason none of these answers worked for me. Here is what did.

axiosInstance.interceptors.request.use(
  function (config) {
    const controller = new AbortController();
    const cfg = {
      ...config,
      signal: controller.signal,
    };
    controller.abort('We gotta cancel this');
    return cfg;
  },
  function (error) {
    return Promise.reject(error);
  },
);

Thing I learned AbortController is a native to javascript/node.

Solution 8:[8]

My solution based on https://stackoverflow.com/a/64228288/2051938

axios.ts

const axiosInstance = axios.create({ baseURL: apiBaseUrl });

axiosInstance.interceptors.request.use(
    req => {
        const originalRequest = req;
        const cancelUniqId = (originalRequest.cancelToken as unknown) as string;

        if (Object.hasOwnProperty.call(currentExecutingRequests, cancelUniqId)) {
            const source = currentExecutingRequests[cancelUniqId];
            delete currentExecutingRequests[cancelUniqId];
            source.cancel();
        }

        if (cancelUniqId) {
            const CancelToken = axios.CancelToken;
            const source = CancelToken.source();
            originalRequest.cancelToken = source.token;
            currentExecutingRequests[cancelUniqId] = source;
        }

        return originalRequest;
    },
    err => {
        return Promise.reject(err);
    }
);

axiosInstance.interceptors.response.use(
    response => {
        for (const key of Object.keys(currentExecutingRequests)) {
            if (currentExecutingRequests[key].token === response.config.cancelToken) {
                delete currentExecutingRequests[key];
                break;
            }
        }
        return response;
    },
    error => {
        const { response } = error;

        if (axios.isCancel(error)) {
            return new Promise(() => {
                //
            });
        }

        for (const key of Object.keys(currentExecutingRequests)) {
            if (currentExecutingRequests[key].token === response.config.cancelToken) {
                delete currentExecutingRequests[key];
                break;
            }
        }

        return Promise.reject(error);
    }
);

export { axiosInstance };

Usage:

axiosInstance.request({
    url: "some/req/path",
    method: "POST",
    params: {...},
    data: {...},
    cancelToken: "someUniqRequestID" // <-- IMPORTANT!
})

as a result, all requests with someUniqRequestID token will be cancelled when previous request with SAME cancelToken was not finished before.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 grane2212
Solution 2 Kirill Taletski
Solution 3 jaycethanks
Solution 4 Vijay sadhu
Solution 5 Luciano Corniglione
Solution 6 MarkWPiper
Solution 7 Thomas Valadez
Solution 8 mixalbl4