Search code examples
javascriptnode.jsspotify-app

node.js function call order, Spotify Web API


I'm trying to use Spotify Web API with a wrapper for node.js. Before I do any of the API calls, I need to set the access token. In my script, I call the setAccessToken function first, and then call some API request below. However, due to the async nature of node.js my setAccessToken method is called after my API requests, which results in failed API requests. How can I make sure my setAccessToken function returns before I call API requests below (API request lines start with spotifyApi.getMySavedTracks call)

app.get('/callback', (req, res) => {
  var code = req.query.code || null;
  if (code === null) {
      console.log("code is null");
  }

  async function authorize() {
    const data = await spotifyApi.authorizationCodeGrant(code);
    // console.log('The token expires in ' + data.body['expires_in']);
    // console.log('The access token is ' + data.body['access_token']);
    // console.log('The refresh token is ' + data.body['refresh_token']);
    // Set the access token on the API object to use it in later calls
    accesToken = data.body['access_token'];
    refreshToken = data.body['refresh_token'];
    spotifyApi.setAccessToken(accesToken);
    spotifyApi.setRefreshToken(refreshToken);
  }

  try {
    authorize();
  } catch (err) {
    console.log('Something went wrong!', err);
  }

  spotifyApi.getMySavedTracks({
    limit : lim,
    offset: 1
  })
  .then(function(data) {
    return data.body.items;
  }, function(err) {
    console.log('Something went wrong!', err);
  })
  .then(function(items) {
    let ids = [];
    items.forEach(function(item) {
      ids.push(item.track.id);
    })
    return spotifyApi.getAudioFeaturesForTracks(ids); //2xc8WSkjAp4xRGV6I1Aktb
  })
  .then(function(data) {
    let songs = data.body.audio_features;
    //console.log(songs);
    let filteredSongURIs = [];
    songs.forEach(function(song) {
      if ((song.energy >= moodScore-offset) && (song.energy <= moodScore+offset)) {
        filteredSongURIs.push(song.uri);
      }
    })

    //Create Playlist with filtered songs
    spotifyApi.createPlaylist('Mooder Playlist', { 'description': 'My description', 'public': true })
    .then(function(data) {
      return data.body.id;
    }, function(err) {
      console.log('Something went wrong!', err);
    })
    .then(function(playlist) {
      spotifyApi.addTracksToPlaylist(playlist, filteredSongURIs)
    })
    .then(function(data) {
      console.log('Added tracks to playlist!');
    }, function(err) {
      console.log('Something went wrong!', err);
    });
    
  }, function(err) {
    console.log(err);
  });


  res.redirect('/testAPI');
});

Solution

  • You can wrap all your code after setAccessToken into another then, or you can await that Promise to end:

    // Add `async` here
    app.get('/callback', async (req, res) => {
      var code = req.query.code || null;
      if (code === null) {
          console.log("code is null");
      }
      try {
        const data = await spotifyApi.authorizationCodeGrant(code);
        // console.log('The token expires in ' + data.body['expires_in']);
        // console.log('The access token is ' + data.body['access_token']);
        // console.log('The refresh token is ' + data.body['refresh_token']);
        // Set the access token on the API object to use it in later calls
        accesToken = data.body['access_token'];
        refreshToken = data.body['refresh_token'];
        spotifyApi.setAccessToken(accesToken);
        spotifyApi.setRefreshToken(refreshToken);
      } catch (err) {
        console.log('Something went wrong!', err);
      }
    
      // The rest of your code