Search code examples
node.jsapiasynchronouspromisenode-fetch

incorrect behaviour of node-fetch while trying to get token from an api


I want to connect to an external api using some node-fetch code. My code first sends the login details & should receive a token from the api. Then this token is used for all the later communications.

Here is the code :

import fetch from 'node-fetch';
function getTokenForAuth(info) {
    try {
        var auth_token = '';
        fetch(api_url + '/api/api-token/', {
            method: 'POST',
            body: JSON.stringify(info),

            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        })
        .then(function(res) {
            return res.json();
        })
        .then(function(json) {
            auth_token = json;
        })
        return auth_token.token;
    }

    catch (e) {
        console.log('[-] Error: Token Not Received');
        console.log('[!] Exception: ' + e);
    }
}

function getJSONFromRelativeURL(relativeURL, info) {
    return fetch(`${api_url}${relativeURL}`, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Token ' + getTokenForAuth(info)
        }
    })
    .then(function(res) {
        console.log(res);
        return res.json();
    })
    .then(function(json) {
        console.log(json);
    })
}

In the getJSONFromRelativeURL() function's request headers, if I hardcode the token, I get correct results. But if I run the code as it is now, I get an error saying : { detail: 'Invalid token.' }.

I think this is because of async nature of the promise in the fetch function, because of which it sometimes isnt able to send the token in time before the getJSONFromRelativeURL() gets called. I am not sure about this hypothesis & don't know how to correct this.


Solution

  • Your problem is here:

    .then(function(json) {
        auth_token = json;
    })
    return auth_token.token;
    

    Your return statement is outside the Promise chain. This means that, at the point you hit return, the fetch request hasn't had a chance to even run yet. You've essentially just told the fetch Promise chain what to do when it does return.

    So essentially

    I think this is because of async nature of the promise in the fetch function, because of which it sometimes isnt able to send the token in time before the getJSONFromRelativeURL() gets called.

    is 100% correct.

    What you'll need to do is restructure things a little bit:

    function getTokenForAuth(info) {
      return fetch(api_url + "/api/api-token/", {
        method: "POST",
        body: JSON.stringify(info),
    
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json"
        }
      }).then(function(res) {
        return res.json();
      });
    }
    
    function getJSONFromRelativeURL(relativeURL, info) {
      return getTokenForAuth(info)
        .then(function(token) {
          return fetch(`${api_url}${relativeURL}`, {
            method: "GET",
            headers: {
              "Content-Type": "application/json",
              Authorization: `Token ${token}`
            }
          });
        })
        .then(function(res) {
          console.log(res);
          return res.json();
        })
        .then(function(json) {
          console.log(json);
        });
    }