Facebook Real-time Updates in Rails on Heroku

Adam Duke | 03 April 2012

One of our recent client apps depends heavily on accurate and up to date data from the Facebook Graph API. The first solution that we implemented relied on polling the API on a scheduled basis, but that proved not to be very scalable as the number of users in the system increased.

The solution was to use Facebook’s Real-time Updates feature. The general concept of the Real-time Updates feature is that an application subscribes to updates for a particular type of object (users, permissions, or pages). When one of those objects changes within Facebook, Facebook will make an http POST request to your application with data about which objects and which fields of those objects have changed. The application can then make requests to the Graph API to get the updated data.

At first glance that seems simple enough, however, there are a few minor details that need to be addressed to get things working. Those details mostly have to do with successfully creating the subscription. Creating the subscription requires making a POST request to

https://graph.facebook.com/<app-id>/subscriptions?access_token=...

The body of your request should contain parameters for:

  • object – the type of object you are interested in updates about
  • fields – a comma separated list of fields on the object you are interested in updates about
  • callback_url – the url that Facebook will post updates to
  • verify_token – a token that will be echoed back during verification of your callback

The first detail to be aware of is that Facebook is going to make a GET request to your callback url to verify it exists, so passing

http://localhost:3000/facebook_callback

isn’t going to work. Facebook needs to be able to get to your url. In our case this meant testing the external integration in a staging environment.

http://my-cool-app-staging.heroku.com/facebook_callback

The second detail to be aware of is that the application needs to be able to handle two concurrent threads. The subscription request to Facebook is the first thread and will block waiting for Facebook’s response. Before Facebook creates the subscription they will call your callback url to verify it, this is the second thread. In the Heroku world, this means you have to have at least two dynos available for the subscription to succeed.

The final detail to be aware of is that Facebook is expecting a particular response when it attempts to verify your callback url. The verification request will have three parameters:

  • hub.mode – this is always the string “subscribe”
  • hub.challenge – a random string
  • hub.verify_token – the verify_token that was passed in the subscription request

The callback should verify that the incoming verify_token is the same as the token that was passed during the subscription request. If they are the same, the callback should return a 200 OK response with a content type of “text/plain” and a body containing the value of the hub.challenge parameter.

If all goes well, your application should now receive an “application/json” encoded POST request when Facebook notices changes to the objects you are interested in.

Because the application we were working on used Facebook so heavily, we decided to use a gem that nicely wraps the Facebook Graph API. The gem is named Koala and provides some convenient methods to help in setting up the subscriptions. The entire content of the subscription and verify steps are shown below:

 1 class FacebookRealtimeUpdatesController < ApplicationController
 2 
 3   def verify
 4     challenge = Koala::Facebook::RealtimeUpdates.meet_challenge(params, FACEBOOK_REALTIME_UPDATE_VERIFY_TOKEN)
 5     respond_to do |format|
 6       format.text do
 7         render text: challenge
 8       end
 9     end
10   end
11 
12   def update
13     # TODO: Handle an incoming POST from Facebook
14     head :ok
15   end
16 
17   def subscribe
18     updates = Koala::Facebook::RealtimeUpdates.new(:app_id => FACEBOOK_APP_ID, :secret => FACEBOOK_APP_SECRET)
19     updates.subscribe("user", "friends, activities, interests, music, books, movies, likes", FACEBOOK_REALTIME_UPDATE_CALLBACK_URL, FACEBOOK_REALTIME_UPDATE_VERIFY_TOKEN)
20     head :ok
21   end
22 end