Search code examples
javascriptreact-nativefetchasyncstorage

Reading token from AsyncStorage before a fetch call


In my ReactNative app, I'm trying to come up with a nice pattern to read the access_token I store in AsyncStorage and use it in a fetch call.

In other words, I want to create a pattern that uses some type of wrapper that makes sure that the fetch call always has the access_token it needs. So execution order should always be:

Invoke Fetch Call -> Get Token from AsyncStorage and Prep Header -> Execute Fetch Call

I came up with the following code but it looks like I'm having problems with the Async part of AsyncStorage and my fetch calls are going out without the token.

Here's my fetch call:

export const someApiCall = (request) => {

   const url = 'https://myapi.com/add';

   return (dispatch) => fetch(url, fetchOptionsPost(request))
        .then((response) => {

            if (response.ok && response.status === 200) {

                // Got data. Dispatch some action

            }
        })
}

Here, I'm using a helper function to prepare the headers, etc. Here's what the fetchOptionsPost() looks like:

export const fetchOptionsPost = (request) => {

    getAccessToken()
        .then(token => {

            return {
                method: 'POST',
                mode: 'cors',
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": "Bearer " + token
                },
                body: JSON.stringify(request)
            }
       });
};

And the getAccessToken() function simply reads it from AsyncStorage as below:

export const getAccessToken = async () => {

    return await AsyncStorage.getItem("access_token");
}

This pattern is NOT working and API calls are going out without a token.

I also want to mention that if I hard-code the token inside my fetchOptionsPost() method, everything works fine. Clearly, the issue here is that the fetchOptionsPost() is not returning anything.

What can I do to make sure that I will ALWAYS have my token in my fetchOptionsPost?


Solution

  • Here's what I've come up with which seems to work fine. I still would appreciate any suggestions or improvements to this code.

    First, here's what my fetch call looks like now. I wrapped it inside the getAccessToken() function which is an async call but because I'm using redux-thunk, I'm able to do this.

    export const someApiCall = (request) => {
    
       const url = 'https://myapi.com/add';
    
       return (dispatch) => getAccessToken()
            .then(token => {
                fetch(url, fetchOptionsPost(request, token))
                    .then((response) => {
    
                         if (response.ok && response.status === 200) {
    
                              // Got data. Dispatch some action
    
                          }
                     })
            })
    }
    

    I slightly changed my fetchOptionsPost() helper function which now accepts the token. It's also a bit more robust now. If it doesn't get a token, it simply omits the Authorization part in the header. I opted for this approach as some calls to my API backend do not require authentication. Also the isValidString() is another helper validation function I created to make sure I do get a valid string. It returns a TRUE or FALSE response based on the string value inputed:

    export const fetchOptionsPost = (data, token = null) => {
    
        if (isValidString(token)) {
    
            return {
                method: 'POST',
                mode: 'cors',
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": "Bearer " + token
                },
                body: JSON.stringify(data)
            }
        } else {
    
            return {
                method: 'POST',
                mode: 'cors',
                headers: {
                    "Content-Type": "application/json"
                },
                body: JSON.stringify(data)
            }
        }
    };
    

    And finally, the getAccessToken() function which didn't really change but here it is:

    export const getAccessToken = async () => {
    
        return await AsyncStorage.getItem("access_token");
    }
    

    As I said, I'd appreciate any comments or suggestions on further improving this code.

    Hope this is useful to others.