Backbone JS – Facebook News Feed Example
update: check out https://github.com/serdary/backbone-facebook-newsfeed for source code.
Backbone.js is a javascript framework and I just started using backbone along with coffeescript for my new 2 projects.
In this tutorial, I’ll develop a simple backbone javascript application that fetches logged in facebook user’s news feed and lets the user share a new status message.
I used koala gem for facebook open graph.
You can use coffeescript instead of pure javascript, I’ll probably add coffeescript version of this simple app in part 2.
Backbone is a great framework if you will create a heavy-js app. It may have some differences than traditional MVC pattern, like views can be used as controllers. But if you know MVC pattern, it won’t take too much time to learn backbone.
I assume you know the basics of rails, so I will not add rails related stuff all the time.
In short, backbone has Model, Collection, Router and View.
Model is just like rails models.
Collection provides some extra functionality to model groups like sorting and filtering.
Router acts as rails controllers and routes definitions to map urls to actions.
Views = rails views. We can (should) use a templating library something like mustache, haml or jquery templates to separate html from javascript views.
I’ll structure js codes in assets/javascript/backbone, just like:
app
–assets
—-javascripts
——backbone
——–models
———-feed.js
——–routers
———-feeds_router.js
——–views
———-feeds
————new_view.js
————show_view.js
————index_view.js
——myfeed.js
——backbone-min.js
——underscore-min.js
p.s: if you’ll use coffeescript, I strongly recommend backbone-rails gem which creates this structure (and more) for you with a simple command.
After creating this folder structure, we need to add some require and require_tree directives to application.js and myfeed.js file.
You can see all the requires directives on github, now lets start the application with a rails controller and feed model.
Lets create first controller, the main entry point of app, home controller with a simple index action and make it homepage via routes.rb file.
Now, change the index.html.erb view under home controller to call our app’s init function.
<h1>
<a href="#">My Facebook Feeds</a>
</h1>
<div id="notice"></div>
<div id="app"></div>
<script type="text/javascript">
$(function() {
App.init();
});
</script>
Model
App.Models.Feed = Backbone.Model.extend({
defaults: {
from_name: null,
from_id: null,
message: null,
feed_type: null,
like_count: null
}
});
App.Collections.Feeds = Backbone.Collection.extend({
model: App.Models.Feed,
url: '/feeds'
});
I just defined the default values for the feed model properties and created a feeds collection which has a model property points Feed and a url points /feeds (we’ll create a feeds controller in a moment)
Router
The only router will be feed_router for this app. We’ll add the routes to call related actions.
App.Routers.FeedsRouter = Backbone.Router.extend({
feeds: {},
initialize: function() {
// create a new object to hold feeds collection
feeds = new App.Collections.Feeds();
},
routes: {
'' : 'index',
'/index': 'index',
'/new' : 'newFeed',
'/:id' : 'show'
},
index: function() {
var view = new App.Views.IndexView(feeds);
$('#app').html(view.render().el);
},
show: function(id) {
console.log('index'+id);
},
newFeed: function() {
console.log('newFeed');
}
});
So after creating this router, if you wanna try your app, you may get “Backbone.history is undefined” error on your console. I have spent 15 mins to resolve that, the reason is you don’t have any routes on your controller. (I had routes but I mistyped as ‘routers’, so that resulted an error)
View
App.Views.IndexView = Backbone.View.extend({
initialize: function() {
this.feeds = this.options.feeds;
},
addAll: function() {
if (this.feeds === undefined || this.feeds.length < 1) {
$(this.el).find("tbody").append('no feeds yet');
return;
}
// TODO: use haml-js or jquery templates
var view = '';
var template = '<tr><td>[from_name]</td><td>[message]</td><td>[feed_type]</td><td>[like_count]</td><td><a href="#/[id]">Show</td></tr>';
for (var i = 0; i < this.feeds.models.length; i++) {
var feed = this.feeds.models[i].attributes;
view += template.replace('[from_name]', feed.from_name).replace('[message]', feed.message)
.replace('[feed_type]', feed.feed_type).replace('[like_count]', feed.like_count).replace('[id]', feed.id);
}
$(this.el).find("tbody").append(view);
},
render: function() {
var table = '<h1>Feeds</h1><a href="#/new">New Feed</a><table><tr><th>From</th><th>Message</th><th>Type</th><th>Like count</th></tr></table>';
$(this.el).html(table);
this.addAll();
return this; // chain
}
});
At this point, you should get the index view with an empty table and a warning, as ‘no feeds yet’.
So it’s clear that we need feeds to try index page and show page. I could add facebook open graph related codes here but it may distract the backbone subject, so I’ll just create a feeds controller and a dummy json feeds response for now.
Feeds Controller
class FeedsController < ApplicationController
def index
#dummy
feeds = [{ :id => 1, :from_name => 'dummy from1', :message => 'dummy message1', :feed_type => 'dummy feed_type1',
:like_count => 'dummy like_count 1' },
{ :id => 2, :from_name => 'dummy from2', :message => 'dummy message2', :feed_type => 'dummy feed_type2',
:like_count => 'dummy like_count 2' }]
render :json => feeds
end
def create
end
end
Now, we should have a working feed listing page. Let’s add a separate show page for items.
Show View
App.Views.ShowView = Backbone.View.extend({
render: function() {
var template = '<b>From name:</b><em>[from_name]</em><b>message:</b><em>[message]</em><a href="#">home</a>';
var view = template.replace('[from_name]', this.model.attributes.from_name).replace('[message]', this.model.attributes.message);
$(this.el).html(view);
return this;
}
});
Router – Show Action
App.Routers.FeedsRouter = Backbone.Router.extend({
...
show: function(id) {
var feed = feeds.get(id);
var view = new App.Views.ShowView({ model: feed });
$('#app').html(view.render().el);
}
...
});
Now lets implement javascript part of the create action, then we need to start coding facebook open graph stuff to finalize the app.
New View
App.Views.NewView = Backbone.View.extend({
initialize: function() {
this.collection = this.options.collection;
this.model = new this.collection.model();
},
events: {
'submit #new-feed' : 'save'
},
render: function() {
var template = '<h1>New feed</h1><form id="new-feed" name="feed"><div><b> message:</b><input type="text" name="message" id="message" /></div><div><input type="submit" value="Create Feed" /></div></form><a href="#">Home</a>';
$(this.el).html(template);
return this;
},
save: function() {
this.collection.create({ message: $('#message').val() }, {
success: function(feed) {
this.model = feed;
//window.location.hash = '#/' + this.model.id
window.location.hash = '#'
},
error: function(feed) {
}
});
return false;
}
});
Router – New Action
App.Routers.FeedsRouter = Backbone.Router.extend({
...
newFeed: function() {
var view = new App.Views.NewView({ collection: feeds });
$('#app').html(view.render().el);
}
...
});
Connect to Facebook Open Graph
Now we need to let users connect app via their facebook account and fetch their news feed, update their status.
To provide this, I’ll use awesome koala gem.
1. create a new facebook app and get app_id, app_secret.
2. add koala gem to Gemfile
gem “koala”
3. create a new rails controller with connect_fb and fb_callback actions
rails g controller sessions connect_fb fb_callback
4. check if the user is logged in. Do this for all the actions for all controllers, so add a before_filter to ApplicationController method to main controller.
class ApplicationController < ActionController::Base
before_filter :authorize
protect_from_forgery
protected
def authorize
is_user_logged_in
# TODO: redirect to root_url if not authorized
end
private
def is_user_logged_in
@user_logged_in = session[:fb_access_token] != nil
end
end
5. Add a facebook login link to home/index view.
..
<% unless @user_logged_in %>
<%= link_to 'Connect Facebook', sessions_connect_fb_path, { :class => 'fb_connect_btn' } %>
<% end %>
..
6. Add routes for session controller to routes.rb file
Then the only thing left is creating callback action and redirecting user to facebook with a callback url. So here it is:
class SessionsController < ApplicationController
def connect_fb
app_id = '' # Your app_id
app_secret = '' # Your app_secret
session[:oauth] = Koala::Facebook::OAuth.new(app_id, app_secret, sessions_fb_callback_url)
redirect_to session[:oauth].url_for_oauth_code(:permissions => "publish_stream,read_stream")
end
def fb_callback
if params[:code].blank?
redirect_to root_url, :notice => 'You should allow app to connect your Facebook account'
return
end
session[:fb_access_token] = session[:oauth].get_access_token(params[:code])
redirect_to root_url, :notice => 'FB Connected!'
end
end
I've added below code piece to application.js, it is a facebook connect hotfix. When facebook call-backs your app, it adds an hash and some chars, so we need to remove it to start our js app.
$(function() {
// fb connect redirect hotfix
if (window.location.hash === '#_=_') {
window.location.hash = '';
}
});
Now our app can connect to facebook, so we can actually fetch user's news feed and post user's status messages to facebook open graph API.
FeedsController - working with real facebook news feed data
class FeedsController < ApplicationController
def index
unless session[:fb_access_token]
render :json => {}
return
end
begin
graph = Koala::Facebook::API.new(session[:fb_access_token])
result = graph.get_connections('me', 'home')
feeds = create_feed_objects(result)
rescue Exception => ex
session[:fb_access_token] = nil
logger.error "Exception - #{ex}"
end
render :json => feeds
end
private
def create_feed_objects(result)
feeds = []
result.each do |feed|
from_name = feed['from'].is_a?(Hash) ? feed['from']['name'] : ''
from_id = feed['from'].is_a?(Hash) ? feed['from']['id'] : ''
like_count = feed['likes'].is_a?(Hash) ? feed['likes']['count'] : 0
message = (feed['type'] == 'link' or feed['message'].blank?) ? feed['story'] : feed['message']
feeds.push({ :id => feed['id'], :from_name => from_name, :from_id => from_id, :like_count => like_count,
:feed_type => feed['type'], :message => message })
end
feeds
end
end
FeedsController - post status messages to facebook open graph
class FeedsController < ApplicationController
...
def create
return unless session[:fb_access_token]
begin
graph = Koala::Facebook::API.new(session[:fb_access_token])
obj_id = graph.put_wall_post(params[:feed][:message])
#feed = create_feed_objects(graph.get_object(obj_id))[0]
feed = {}
rescue Exception => ex
session[:fb_access_token] = nil
logger.error "Exception - #{ex}"
end
render :json => feed
end
...
So with these latest changes, a simple backbone javascript mvc app is ready.
It displays the news feed of logged in user, post new status messages as logged in user, by using facebook open graph API.
koala gem is used for facebook graph calls and authentication, backbone framework is used to organize the js code.
Please check the source code of backbone news feed on github, and let me know your thoughts!
Tags: backbone, coffeescript, facebook, js, koala, mvc, rails 3.1, Ruby on Rails

cool, is there any demo available for the same.
Regards
sorry, no demo available.
Cloned the app and set my app_id and app_secret in app/controllers/sessions_controller.rb. When I click the facebook connect link I get the error:
{
“error”: {
“message”: “Invalid redirect_uri: Given URL is not allowed by the Application configuration.”,
“type”: “OAuthException”,
“code”: 191
}
}
Any ideas?
It seems like you haven’t configured your test facebook app urls. Can you check them and let me know?
Hey there, You’ve done a fantastic job. I’ll certainly digg it and in my view suggest to my friends. I am confident they’ll be benefited from this site.