Facebook Node SDK example

I’ve been writing some Node.js software to interact with Facebook recently. To do this, I just picked the first SDK listed in the Facebook developer’s page for Node.js. However, I couldn’t find a good example listing of how to use this SDK to iterate over multiple pages of results. So, this is a quick post that will hopefully serve as such an example.

The complete Node.js application can be downloaded from Github at https://github.com/aesidau/sbs-headlines, but I’ll walk through it step by step here. The application will list the first 1,000 news headlines from the SBS News page on Facebook. However, before any of this will work, you will need to get the fb and async modules, so start with something like:

npm install fb
npm install async

Also, I assume that you’ve set up a Facebook application over at https://developers.facebook.com/ and have its app id and app secret to hand. In fact, for this Node.js example, I am assuming that these values are stored in the environment variables FB_APP_ID and FB_APP_SECRET, respectively.

The first step in any Facebook API application is to use these app credentials to get an access token that can be used in later Facebook API calls. In this example, I’m going to get a basic access token that isn’t associated with a Facebook user, so will be able to obtain only public information. That’s all we need here, anyway.

// Acquire a new access token and callback to f when done (passing any error)
function acquireFacebookToken(f) {
  FB.napi('oauth/access_token', {
    client_id: process.env.FB_APP_ID,
    client_secret: process.env.FB_APP_SECRET,
    grant_type: 'client_credentials'
  }, function (err, result) {
    if (!err) {
      // Store the access token for later queries to use
      FB.setAccessToken(result.access_token);
    }
    if (f) f(err);
  }); // FB.napi('oauth/access_token'
}

This has been written as a function so we can call it later. Note that it uses a callback to indicate to the caller when the process has been completed, passing back any errors that came up.

So far, so obvious. Now that the access token is sorted, let’s look at what is required to iterate over a Facebook feed using the API.

I’m going to use the doUntil function in the async module. This enables iteration of functions that return results in callbacks. The other thing to note is that each call to the Facebook API will return a “paging” object that will contain a “next” attribute but only if there is another page of results to retrieve. This attribute can be parsed to construct the next Facebook API query to obtain the next page of results.

I have also included a test for if the access token has expired. This shouldn’t happen in a simple app where the access token was only just acquired. However, in many apps, the access token may have been acquired hours before. So, if this code is to be reused, it’s a good idea to deal with this case.

// Process the Facebook feed and callback to f when done (passing any error)
function processFacebookFeed(feed, f) {
  var params, totalResults, done;

  totalResults = []; // progressively store results here
  params = { // initial set of params to use in querying Facebook
    fields: 'message,name',
    limit: 100
  };
  done = false; // will be set to true to terminate loop
  async.doUntil(function(callback) {
    // body of the loop
    FB.napi(feed, params, function(err, result) {
      if (err) return callback(err);
      totalResults = totalResults.concat(result.data);
      if (!result.paging.next || totalResults.length >= 1000) {
        done = true;
      } else {
        params = URL.parse(result.paging.next, true).query;
      }
      callback();
    }); // FB.napi
  }, function() {
    // test for loop termination
    return done;
  }, function (err) {
    // completed looping
    if (err && err.type == 'OAuthException') {
      // the access token has expired since we acquired it, so get it again
      console.error('Need to reauthenticate with Facebook: %s', err.message);
      acquireFacebookToken(function (err) {
        if (!err) {
          // Now try again (n.b. setImmediate requires Node v10)
          setImmediate(function() {
            processFacebookFeed(f);
          }); // setImmediate
        } else if (f) {
          f(err);
        }
      }); // acquireFacebookToken
    } else if (f) {
      f(err, totalResults);
    }
  }); // async.doUntil
}

Lastly, we just need to wire these two functions together so that we get the access token, retrieve the results (i.e. the headlines from SBS World News Australia), and then print them out.

acquireFacebookToken(function (err) {
  if (err) {
    console.error('Failed authorisation to Facebook: %s', err.message);
  } else {
    console.log('Acquired Facebook access token');
    // Now let's do something interesting with Facebook
    processFacebookFeed('SBSWorldNewsAustralia/feed', function (err, results) {
      if (err) {
        console.error('Failed to retrieve Facebook feed: %s', err.message);
      } else {
        // Print out the results
        results.forEach(function (i) {
          var headline = i.message || i.name;
          // If it's an embedded video, possible there's no headline
          if (headline) {
            console.log(headline);
          }
        }); // results.forEach
      }
    }); // processFacebookFeed
  }
}); // acquireFacebookToken

And that’s it. I hope this has been useful for others. Grab the complete application from GitHub to try it out, but make sure you set up your environment variables for the App ID and App Secret first.