Search code examples
reactjsreduxreact-reduxredux-toolkitrtk-query

RTK query interceptor multiple requests


I am having a problem with the RTK query interceptor, I implemented Automatic re-authorization by extending fetchBaseQuery​ per RTK query documentation and now I have a problem that if multiple requests get fired and the token is not valid all of them will get 401 as a response and all of them will try to refresh a token which will result in the first one being successful and other ones will fail and then the else will fire and return the user to log in screen.

Is there a way to prevent this?

  let result = await baseQuery(args, api, extraOptions)
      if (result.error && result.error.status === 401) {
        // try to get a new token
        const refreshResult = await baseQuery(
          {
          url: `http://.../${refreshToken}`,
          method: 'POST'
          }, 
          api, 
          extraOptions
        );
        if (refreshResult.data) {
          // store the new token
          api.dispatch(tokenReceived(refreshResult.data))
          // retry the initial query
          result = await baseQuery(args, api, extraOptions)
        } else {
          api.dispatch(loggedOut())
        }
      }
      return result

Solution

  • One possible solution for that would be to use a mutex. We were discussing a possible solution for that in this github discussion:

    import { Mutex } from 'async-mutex';
    
    const mutex = new Mutex();
    
    const baseQueryWithReauth = async (args, api, extraOptions) => {
      await mutex.waitForUnlock();
      let result = await baseQuery(args, api, extraOptions);
    
      if (result.error && result.error.status === 401) {
        if (!mutex.isLocked()) {
          const release = await mutex.acquire();
    
          try {
            const refreshResult = await baseQuery(
              {
                url: 'auth/refresh/',
                method: 'POST',
                body: { getToken(), getRefreshToken() },
              },
              api,
              extraOptions,
            );
    
            if (refreshResult.data) {
              api.dispatch(tokenUpdated(refreshResult.data));
    
              // retry the initial query
              result = await baseQuery(args, api, extraOptions);
            } else {
              api.dispatch(logout());
            }
          } finally {
            release();
          }
        } else {
          await mutex.waitForUnlock();
          result = await baseQuery(args, api, extraOptions);
        }
      }
    
      return result;
    };
    
    const baseApi = createApi({
      baseQuery: baseQueryWithReauth,
      endpoints: () => ({}),
    });