0

I am trying to add RETRY Logic in the context of - I make an API call -> response is 401 -> I invoke APi to request for a NEW Token in the background. The poin there si MY API Calls shouldnt fail. Following is my API File (This is common - Every API in my application invokes this File to make an FETCH)

NOTE : I have seen articles using the fetch().then() approach, but we are using YIELD.

Specific API File -

// apiRequest = part of api.js file i am specifying below

const response = yield retry(3,1000,apiRequest,options); // My apiRequest while trying for getting new access tokens send me a NULL, do we want that ?  
  if (undefined !== response && null !== response) {
    const formattedResponse = yield apply(response, response.json);
    if (response.status === 200) {
      yield call(handleAddCampaignResponseSuccess, formattedResponse);
    } else {
      yield call(handleAddCampaignResponseFailure, formattedResponse);
    }
  } else{
     // Show some Message on UI or redirect to logout  
  }

// api.js

function* apiRequest(options) {
  const { method, body, url } = options;
  const accessToken = yield select(selectors.AccessToken);
  const idToken = yield select(selectors.IdToken);

  try {
    var response = yield call(fetch, url, {
      method: method,
      body: body,
      headers: {
        "Content-Type": ContentTypes.JSON,
        Authorization:
          accessToken != "" ? `Bearer ${accessToken} ${idToken}` : "",
      },
    });
    if (null !== response) {
      if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
        // Unauthorized requests - redirect to LOGOUT
        // Request for Refresh Token !
        yield put(refreshTokenOnExpiry());
        return null; // Is this necessary
      } else if (response.status === HTTP_CODES.HTTP_NOT_FOUND) {
        return null;
      } else if (response.status === HTTP_CODES.HTTP_SERVER_ERROR) {
        // Logout cos of serrver error
        yield put(handleLogout());
        return null;
      } else {
        console.log("From Else part");
        // - Called on intent to ensure we have RESET redirections and that it does not cause issues of redirection.
        yield put(resetRedirections());
        return response;
      }
    } else {
      // Handle Logout
      yield put(stopTransition());
      yield put(handleLogout());
      
    }
  } catch (error) {
    // Cors Error in case of DEV URL
    // See if SAGA is Still listening to the Action Dispatches
    console.log("From CATCH BLOCK");
    yield put(stopTransition());
    yield put(handleLogout());
    return null;
  }
}

My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401

API.JS is the file invoked by ALL API's across my website. Also, how can I ensure that refreshTokenOnExpiry gets called ONLY once (meaning at a time there will be multiple API calls and each one when got a 401 will eventually invoke refreshTokenOnExpiry this API)

I am new to generator functions, so I am sure I must have goofed up somewhere.

Also if anyone who can help me build this code correctly, would be great help. Thanks !

Adding Image for reference - I want the FAILED API's to be retried which aint happening :

enter image description here

Gauri Padbidri
  • 371
  • 4
  • 15

1 Answers1

0

My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401

Scroll down to the section "Retrying XHR calls" in the redux-saga recipes to get an idea of what the retry effect is doing behind the scenes.

The retry effect can be used on any function, no just an API call, so it's not looking at the response code. It defines "failure" as code that throws an error rather than completing execution. So what you need to do is throw an error in you apiRequest.

No guarantees, but try this:

if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
    // Unauthorized requests - redirect to LOGOUT
    // Request for Refresh Token !
    yield put(refreshTokenOnExpiry());
    throw new Error("invalid token");
}

You need to figure out how to make sure than the new token gets set before retrying. You might want to build your own chain of actions rather than relying on retry. For example, you can put an action with type "RETRY_WITH_NEW_TOKEN" that has a payload containing the original options and the token that it was tried with. That way you can compare it against the token in state to see if you have a new one.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102