Our Blog

0

If you want to retrieve public data from Facebook (typically a Page) you nowadays will have to use Facebook Graph API and thus provide an access-token. In the old days you could use some of the available feeds (following the RSS-principles) but that was back then.
I’ve seen some strange hacks around, but it’s quite simple to pull public data with a non-expiring access-token. (Impatient ones go here!)

Access-tokens in short

The idea behind access-tokens is that the system you’re communicating with ‘knows something about somebody’; is that somebody a valid logged-in user, what is that user allowed to do, etc. For every request your application makes, on behalf of that user, you pass that access-token to the system.
Access tokens typically expire; to reduce the chance of getting in the wrong hands (Facebook offers appsecret_proof to prevent hijacking). When the user uses the application access-tokens get renewed in the background, when the user doesn’t use the application before the access-token expires, a new login / authorization action will provide a new one.
Facebook Graph API knows short- and long-term access-tokens, where the short-term tokens are used by the application’s client and the long-termed by the application’s server (using an app-secret).

Let the app do the talking

The access-tokens mentioned above are “user access-tokens”. If you just want to pull out some public data, having users logging in and expiring access tokens is probably not what you want. But you can also in make requests to Facebook’s API on behalf of an a Facebook App, in stead of a user, working with an “app access-token”.
When you create an app at Facebook for developers, you have an app-id and an app-secret. In stead of requesting “oauth” for an app access-token, you can construct one yourself  by combining your app-id and app-secret, when requesting from a server (not client!!), like this:

https://graph.facebook.com/endpoint?key=value&access_token=app_id|app_secret

Note: using “appsecret_proof” doesn’t make much sense here

Let’s get practical

I needed to display posts from a Facebook Page via Javascript (with Ember.js to be exact). No further Facebook interaction was needed, so user access tokens were not what I was waiting for, I needed an access token that does not expire, and that I’m in control of, not some user. So I opted for using the “app_id|app_secret”-solution with an app access-token, mentioned above. That I had set up as a proxy on my webserver. The flow now is: Client -> Webserver -> FB API -> Webserver -> Client.

Flow proxy fb api permanent access-token

 

Here’s the method I used at the webserver (in PHP) to make calls to Facebook Graph API

/**
* Retrieves response from an call
* to Facebook Graph API
* on behalf of an Facebook app.
*
* Returns a JSON-string when $returnJson is true,
* when false it returns an object of the
* JSON decoded data.
*
* The object-parameter determines which FB object you
* are requesting (posts, photos etc)
*
* The fields-paramater determines which fields
* of that object should be included in your data
*
* Try things out at the FB Graph API explorer tool first:
* https://developers.facebook.com/tools/explorer
*
* @param string $fbUser (id of fb-user to pull from)
* @param string $appId
* @param string $appSecret
* @param string $appApiVersion
* @param string $object (what object to request. Default: posts)
* @param array $fields (what fields of the object to request. Default: message, created_time)
* @param boolean $returnJson (return JSON-string otherwise object. Default: true)
* @return string / object
*/

public function getFacebookPosts(
$fbUser,
$appId, $appSecret, $appApiVersion,
$object='posts',
$fields=array('message','created_time'),
$returnJson=true
)
{
$fieldsString = implode(",", $fields);
$accessToken = sprintf("%s|%s", $appId, $appSecret);

$fbApiUrl = sprintf("https://graph.facebook.com/%s/%s?fields=%s{%s}&access_token=%s", $appApiVersion, $fbUser,$object, $fieldsString, $accessToken);

$json = file_get_contents($fbApiUrl);
return $returnJson ? $json : json_decode($json);
}

Load the Facebook data in Ember.js

For the ones interested in how you easily can work with the requested Facebook data in Ember, stay tuned and I’ll provide a snippet for your serializer.
Of course we’re using Ember Data to handle all this. First of all, model your data by adding a new model. You will at least have to define those attributes corresponding the fields-parameter from your API request.
For example ‘message’,’created_time’. Now setup your adapter, connecting to your ‘proxied webservice’.
When the data comes back to your client and back to Ember, Facebook has a slight different JSON-format than Ember expects, so we have to serialize it a little.
And what Ember expects depends whether you’re using the ‘old’ or ‘new’ adapter for handling JSON.
The previous de facto standard was the Ember Data RESTAdapter, the current de facto standard is the Ember Data v2 JSONAPIAdapter. What do they expect:

  • RESTAdapter: {“modelNamePlural”:[…]}
  • JSONAPIAdapter: {“data”:[…]}

Ember Data RESTSerializer (using the extractArray method)

export default DS.RESTSerializer.extend({

extractArray: function (store, primaryType, payload) {
const posts = payload.posts.data;
const newPayload = { posts: posts };
return this._super(store, primaryType, newPayload);
}

});

 

Ember Data JSONAPISerializer (using the normalizeArrayResponse method)

export default DS.JSONAPISerializer.extend({

normalizeArrayResponse: function (store, primaryType, payload, id, requestType) {
const posts = payload.posts.data;
const newPayload = { data: posts };
return this._super(store, primaryType, newPayload, id, requestType);
}

});

 

This is the model I’m using
Model

export default DS.Model.extend({

message: DS.attr('string'),
created_time: DS.attr('date'),
status_type: DS.attr('string'),
comment_count: DS.attr('number', {defaultValue: 0}),
like_count: DS.attr('number', {defaultValue: 0}),
share_count: DS.attr('number', {defaultValue: 0}),
link: DS.attr('string'),
picture: DS.attr('string'),
caption: DS.attr('string'),
description: DS.attr('string'),
name: DS.attr('string')

});

The fields comment_count, like_count and share_count do not exist in the response from Facebook, they’re nested in ‘comments’, ‘likes’ and ‘shares’ objects. With the serializer’s normalize method you can make the response-data fit into your model.
Another thing I did here is adjusting the date-format given by Facebook, as Moment.js by default will fail with that input on Safari and IE, when there’s no colon in the time-zone.

Add this method to your serializer (either RESTSerializer or JSONAPISerializer) to get the model straight:

normalize : function (modelClass, resourceHash, prop) {
// likes
if( resourceHash.likes.summary.total_count ) {
resourceHash.like_count = resourceHash.likes.summary.total_count;
}
// comments
if( resourceHash.comments.summary.total_count ) {
resourceHash.comment_count = resourceHash.comments.summary.total_count;
}
// shares
if( resourceHash.shares && resourceHash.shares.count ) {
resourceHash.share_count = resourceHash.shares.count;
}

// dates
// prevent "2016-02-16T12:59:03+0000" from failing in moment.js
// convert them to "2016-02-16T12:59:03+00:00"
// (note colon in milliseconds)
resourceHash.created_time = resourceHash.created_time.replace(/([A-Z0-9:-]\+[0-9]{2})([0-9]{2})/, "$1:$2");

return this._super(modelClass, resourceHash, prop);
}