Search code examples
javascriptinterceptor

Interceptor for fetch and fetch retry? (Javascript)


I am trying to create an interceptor for fetch in javascript (React to be more specific). It should get the result from every fetch that gets called, and if it is an 401 error it should initiate a new fetch call to another route to get a cookie (a refresh token). Then, the original fetch call should be tried again (because now the user is logged in).

I have managed to trigger the new fetch call and send back the cookie for each, but I got these two problems below:

  1. I do not now how to retry the fetch call after the refresh token has been recieved. Is that possible? I found the fetch-retry npm (https://www.npmjs.com/package/fetch-retry) but not sure how and if I can implement that on an interceptor when it should be done for the original fetch call.

  2. I seem to be doing something wrong with async await (I think), because the intercept is not waiting for the fetch call before returning the data (the statuscode on the original fetch seems to be 401 and not 200 which it should be after we get the cookie. I also tried to return the response of the fetch inside the interceptor but that returned undefined).

Any idea about how to solve this? Anyone who have done something similar?

Below is my code:

(function () {
  const originalFetch = fetch;
  fetch = function() {
      return originalFetch.apply(this, arguments).then(function(data) {

          if(data.status === 401) {
            console.log('not authorized, trying to get refresh cookie..')

            const fetchIt = async () => {
              let response = await fetch(`/api/token`, {
                  method: 'POST',
                  credentials: 'include', 
                  headers: {
                      'Content-Type': 'application/json'
                  },
              });
          }
        fetchIt();
          } 
         return data

      }); 
  };
})();

EDIT: To make it more clear what I am after. I need an interceptor like I described above to work so I don't have to do something like this after every fetch call:

getData() {
        const getDataAsync = async () => {
            let response = await fetch(`/api/loadData`, { method: 'POST' });

           if(response.status === 401) {
            let responseT = await fetch(`/api/token`, {
                method: 'POST',
                credentials: 'include', 
                headers: {
                    'Content-Type': 'application/json'
                },
            });

            if(responseT.status === 401) {
                return responseT.status
            }

            if(responseT.status === 200) {
            response = await fetch(`/api/loadData`, { method: 'POST' });
            }
           }

          let data = await response.json();
            //Do things with data
        };
        getDataAsync();
    };

So basically the interceptor should:

  1. Check if there is a 401, if so then:
  2. fetch api/token
  3. If api/token returns 401, it should just return that.
  4. If api/token returns 200, it should run original fetch again

Solution

  • You can simple use originalFetch for token and await for response if response is 401 then you simply return empty response to first fetch call else you updated token and then let it go to next condition which will rerun old request.

    let TEMP_API = {
      '401': {
        url: 'https://run.mocky.io/v3/7a98985c-1e59-4bfb-87dd-117307b6196c',
        args: {}
      },
      '200': {
        url: 'https://jsonplaceholder.typicode.com/todos/2',
        args: {}
      },
      '404': {
        url: 'https://jsonplaceholder.typicode.com/todos/1',
        args: {
          method: "POST",
          credentials: "include"
        }
      }
    }
    
    const originalFetch = fetch;
    fetch = function() {
      let self = this;
      let args = arguments;
      return originalFetch.apply(self, args).then(async function(data) {
        if (data.status === 200) console.log("---------Status 200----------");
        if (data.status === 401) {
          // request for token with original fetch if status is 401
          console.log('failed');
          let response = await originalFetch(TEMP_API['200'].url, TEMP_API['200'].args);
          // if status is 401 from token api return empty response to close recursion
          console.log("==========401 UnAuthorize.=============");
          console.log(response);
          if (response.status === 401) {
            return {};
          }
          // else set token
          // recall old fetch
          // here i used 200 because 401 or 404 old response will cause it to rerun
          // return fetch(...args); <- change to this for real scenarios
          // return fetch(args[0], args[1]); <- or to this for real sceaerios
          return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
        }
        // condition will be tested again after 401 condition and will be ran with old args
        if (data.status === 404) {
          console.log("==========404 Not Found.=============");
          // here i used 200 because 401 or 404 old response will cause it to rerun
          // return fetch(...args); <- change to this for real scenarios
          // return fetch(args[0], args[1]); <- or to this for real scenarios
          return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
    sceaerios
        } else {
          return data;
        }
      });
    };
    
    (async function() {
      console.log("==========Example1=============");
      let example1 = await fetch(TEMP_API['404'].url, TEMP_API['404'].args);
      console.log(example1);
      console.log("==========Example2=============");
      let example2 = await fetch(TEMP_API['200'].url, TEMP_API['200'].args);
      console.log(example2);
      console.log("==========Example3=============");
      let example3 = await fetch(TEMP_API['401'].url, TEMP_API['401'].args);
      console.log(example3);
    })();

    1. Example1 request made to api for 404 status which will cause the 404 condition to run which will then call 200 api after which response will be returned
    2. Example2 request made to 200 api which will return 200 status code which will cause 200 condition to pass and run and return response
    3. Example3 request made to api for 401 status which will cause 401 condition to pass which will then call 200 api and print response after which it will fall out of condition where you can set token which will then be used in another fetch request