Stay Classy, Facebook

FBgraph is a nodejs module that provides easy access to the facebook graph api

Oh nooooooesss - MOAR facebook

I created this, because I wanted to access FB's graph from node.
The libraries I found, felt clunky to me, and I needed an excuse to create a node module.

All calls will return json. Facebook sometimes decides to just return a string or true or redirects directly to the image.
I say nay-nay! Let's make it Disney, and keep things consistent!

Installation via npm

$ npm install fbgraph

var graph = require('fbgraph');

Authentication

If you get an accesstoken via some other Oauth module like everyauth , connect-auth or node-oauth you can just set
the access token directly. Most get calls, and pretty much all post calls will require an access_token

graph.setAccessToken(access_token);

This is how you would get authenticated using only the fbgraph module. More details below on the express app section

// get authorization url
var authUrl = graph.getOauthUrl({
    "client_id":     conf.client_id
  , "redirect_uri":  conf.redirect_uri
});

// shows dialog
res.redirect(authUrl);

// after user click, auth `code` will be set
// we'll send that and get the access token
graph.authorize({
    "client_id":      conf.client_id
  , "redirect_uri":   conf.redirect_uri
  , "client_secret":  conf.client_secret
  , "code":           req.query.code
}, function (err, facebookRes) {
  res.redirect('/loggedIn');
});

How requests are made

All calls are made using the request nodejs module
Why? something to do with wheels and re-invention.

Request options are directly mapped and can be set like so:

var options = {
    timeout:  3000
  , pool:     { maxSockets:  Infinity }
  , headers:  { connection:  "keep-alive" }
};

graph
  .setOptions(options)
  .get("zuck", function(err, res) {
    console.log(res); // { id: '4', name: 'Mark Zuckerberg'... }
  });

Possible options can be found on the request github page

followRedirect cannot be overriden and has a default value of false
encoding will have utf-8 as default if nothing is set

Read data from the Graph Api

graph.get("zuck", function(err, res) {
  console.log(res); // { id: '4', name: 'Mark Zuckerberg'... }
});

params in the url

graph.get("zuck?fields=picture", function(err, res) {
  console.log(res); // { picture: 'http://profile.ak.fbcdn.net/'... }
});

params as an object

var params = { fields: picture };

graph.get("zuck", params,  function(err, res) {
  console.log(res); // { picture: "http://profile.ak.fbcdn.net/..." }
});

GraphApi calls that redirect directly to an image will return a json response with relevant fields

graph.get("/zuck/picture", function(err, res) {
  console.log(res); // { image: true, location: "http://profile.ak.fb..." }
});

Search data from the Graph Api

Search for public posts that contain brogramming

var searchOptions = {
    q:     "brogramming"
  , type:  "post"
};

graph.search(searchOptions, function(err, res) {
  console.log(res); // {data: [{id: xxx, from: ...}, {id: xxx, from: ...}]}
});

Publish data to the Graph Api

All publish requests will require an access token

only needs to be set once

graph.setAccessToken(accessToken);

Post a message on a friend's wall

var wallPost = {
  message: "I'm gonna come at you like a spider monkey, chip!"
};

graph.post(userId + "/feed", wallPost, function(err, res) {
  // returns the post id
  console.log(res); // { id: xxxxx}
});

Delete a Graph object

To delete a graph object, provide an object id and the response will return {data: true} or {data:false}

graph.del(postID, function(err, res) {
  console.log(res); // {data:true}/{data:false}
});

Performing a FQL query

A single FQL query is done by sending a query as a string

var query = "SELECT name FROM user WHERE uid = me()";

graph.fql(query, function(err, res) {
  console.log(res); // { data: [ { name: 'Ricky Bobby' } ] }
});

Performing a FQL Multi-Query

FQL Multi-Queries are done by sending in an object containing the separate queries

var query = {
    name:         "SELECT name FROM user WHERE uid = me()"
  , permissions:  "SELECT email, user_about_me, user_birthday FROM permissions WHERE uid = me()"

};

graph.fql(query, function(err, res) {
  console.log(res);
  // { data: [
  //   { name: 'name', fql_result_set: [{name: 'Ricky Bobby'}] },
  //   { name: 'permissions', fql_result_set: [{email: 1, user_about_me: 1...}] }
  // ]}
});

Rockin' it on an Express App

This example assumes that you have a link on the main page / that points to /auth/facebook.
The user will click this link and get into the facebook authorization flow ( if the user hasn't already connected)
After authorizing the app the user will be redirected to /UserHasLoggedIn

/**
 * Module dependencies.
 */

var express = require('express')
  , graph   = require('fbgraph')
  , app     = module.exports     = express.createServer();

// this should really be in a config file!
var conf = {
    client_id:      'YOUR FACEBOOK APP ID'
  , client_secret:  'YOU FACEBOOK APP SECRET'
  , scope:          'email, user_about_me, user_birthday, user_location, publish_stream'
  , redirect_uri:   'http://localhost:3000/auth/facebook'
};

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes

app.get('/', function(req, res){
  res.render("index", { title: "click link to connect" });
});

app.get('/auth/facebook', function(req, res) {

  // we don't have a code yet
  // so we'll redirect to the oauth dialog
  if (!req.query.code) {
    var authUrl = graph.getOauthUrl({
        "client_id":     conf.client_id
      , "redirect_uri":  conf.redirect_uri
    });

    res.redirect(authUrl);

    return;
  }

  // if the `user` hasn't denied the request
  if (req.query.error) {

    // auth `code` is set
    // we'll send that and get the access token
    graph.authorize({
        "client_id":      conf.client_id
      , "redirect_uri":   conf.redirect_uri
      , "client_secret":  conf.client_secret
      , "code":           req.query.code
    }, function (err, facebookRes) {
      res.redirect('/loggedIn');
    });

  } else {
    // handle request denial here:
    res.redirect('/');
  }

});

// user gets sent here after being authorized
app.get('/UserHasLoggedIn', function(req, res) {
  res.render("index", { title: "Logged In" });
});


var port = process.env.PORT || 3000;
app.listen(port, function() {
  console.log("Express server listening on port %d", port);
});

Running tests

Before running the test suite, add your Facebook appId and appSecret to tests/config.js
This is needed to create test users and to get a test access_token

$ npm install
$ make test

Tests might fail if the Facebook api has an issue.

Fork me on GitHub