One of the challenges when building nontrivial Web applications is that JavaScript’s non-directive nature can initially lead to a lack of structure in your code, or in other words, a lack of… backbone. JavaScript is often written as a litany of free-hanging and unrelated blocks of code, and it doesn’t take long before it becomes hard to make sense of the logic and organization of your own code.
Backbone.js is a lightweight framework that addresses this issue by adding structure to JavaScript-heavy Web applications.
Self-contained building blocks
Backbone.js provides several classes (Model, Collection, View, Router) that you can extend to define the building blocks of your application. To build an app with Backbone.js, you first create the Models, Collections, and Views of your application. You then bring these components to life by defining a “Router” that provides the entry points of your application through a set of (deep-linkable) URLs.
With Backbone.js, your code is organized in self-contained entities (Models, Collections, Views): No more free-hanging and unrelated blocks of code.
Data Binding
With Backbone.js, you bind Views to Models so that when a Model’s data changes, all the Views bound to that Model automatically re-render. No more complex UI synchronization code.
Elegant REST Integration
Backbone.js also provides a natural / magical / elegant integration with RESTful services. If your back-end data is exposed through a pure RESTful API, retrieving (GET), creating (POST), updating (PUT), and deleting (DELETE) models is incredibly easy using the Backbone.js simple Model API.
Sample Application
In this three-part tutorial, you’ll create a Wine Cellar application. You can browse through a list of wines, as well as add, update, and delete wines.
- In Part 1 (this post), you define the basic infrastructure. You create a “read-only” version of the application: you’ll be able to retrieve a list of wine and get the details of each wine.
- In Part 2, you add the code to add, update and delete wines. You leverage Backbone’s powerful REST integration.
- In Part 3, you add complete support for history management and deep linking.

NOTE: I also blogged a non-Backbone version of the application here (Java back-end) and here (PHP back-end), which you can look at for comparison.
Part 1: The Read-Only Wine Cellar Application
You can run the application (Part 1) here.
Here is the code:
// Models
window.Wine = Backbone.Model.extend();
window.WineCollection = Backbone.Collection.extend({
model:Wine,
url:"../api/wines"
});
// Views
window.WineListView = Backbone.View.extend({
tagName:'ul',
initialize:function () {
this.model.bind("reset", this.render, this);
},
render:function (eventName) {
_.each(this.model.models, function (wine) {
$(this.el).append(new WineListItemView({model:wine}).render().el);
}, this);
return this;
}
});
window.WineListItemView = Backbone.View.extend({
tagName:"li",
template:_.template($('#tpl-wine-list-item').html()),
render:function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
window.WineView = Backbone.View.extend({
template:_.template($('#tpl-wine-details').html()),
render:function (eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
// Router
var AppRouter = Backbone.Router.extend({
routes:{
"":"list",
"wines/:id":"wineDetails"
},
list:function () {
this.wineList = new WineCollection();
this.wineListView = new WineListView({model:this.wineList});
this.wineList.fetch();
$('#sidebar').html(this.wineListView.render().el);
},
wineDetails:function (id) {
this.wine = this.wineList.get(id);
this.wineView = new WineView({model:this.wine});
$('#content').html(this.wineView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
Code Highlights:
- WineModel (line 2): Notice that we don’t need to explicitly define the attributes (name, country, year, etc). You could add validation, default values, etc. More on that in Part 2.
- WineCollection (lines 4 to 7): “model” indicates the nature of the collection. “url” provides the endpoint for the RESTFul API. This is all that’s needed to retrieve, create, update, and delete wines with Backbone’s simple Model API.
- WineListView (lines 10 to 25): The render() function iterates through the collection, instantiates a WineListItemView for each wine in the collection, and adds it to the wineList.
- WineListItemView (lines 27 to 38): The render() function merges the model data into the “wine-list-item” template (defined in index.html). By defining a separate View for list items, you will make it easy to update (re-render) a specific list item when the backing model changes without re-rendering the entire list. More on that in Part 2.
- WineView (lines 40 to 49): The view responsible for displaying the wine details in the Wine form. The render() function merges the model data (a specific wine) into the “wine-details” template retrieved from index.html.
- AppRouter (lines 52 to 71): Provides the entry points for the application through a set of (deep-linkable) URLs. Two routes are defined: The default route (“”) displays the list of wine. The “wines/:id” route displays the details of a specific wine in the wine form. Note that in Part 1, this route is not deep-linkable. You have to start the application with the default route and then select a specific wine. In Part 3, you will make sure you can deep-link to a specific wine.
Download
The source code for this application is hosted on GitHub here. And here is a quick link to the download.
You will need the RESTful services to run this application. A PHP version (using the Slim framework) is available as part of the download.
UPDATE (1/11/2012): A version of this application with a Java back-end (using JAX-RS and Jersey) is also available on GitHub here. You can find more information on the Java version of this application here.
Part 2 is available here.


Just an FYI, when I click to view a wine I end up with this in the URL:
http://www.coenraets.org/backbone-cellar/part1/#wines/11
If I cut and paste that into a new tab, it is throwing an error and not loading the wine. (Well, in console anyway
Ray,
As mentioned in the post, this is the normal behavior in Part 1. Part 3 of this tutorial covers deep linking.
Christophe
Doh! Helps if I read closer.
Where are the adobe flex posts?
Hello Christophe,
thanks, your blog is top quality.
I hope you’ll focus back to Flex soon!
I will definitely try this one. My prefered client side implementation of those posts
Christophe,
thanks for this tutorial. It would be great of Ray could add the CF backend to this
What IDE do you use or recommend for JS development?
Regards,
Stefan
Aptana is a great IDE for front-end development. Back-end too but it supports javascript code completion very well.
Great tutorial.
I’ve read only Ruby on Rails RESTful services.
php approach is great
I am waiting for part 2.
Dimitris
from Thessaloniki Greece
@jose, @JCLang… Thanks. I have a couple a Flex posts lined up. Stay tuned.
@Stefan. Still using TextMate for now.
what if your are not using a restful service to connect?
I’m curious about that too. It doesn’t seem to work without a rest service…
Great tutorial. really helped me a lot.
I noticed in your demo every time you choose a wine and then hit the back button you are making another call for data. any thoughts? I added code that checks for the collection b4 calling fetch. Am i missing something?
thanks again for this great work!!!
The link to the part 2 of the tutorial seems broken,
@Hecky: Thanks. Fixed.
Thanks for the great post; I’m really enjoying the topics you’re working through. On this app I’m having a bit of trouble with the PHP. Do you happen to know if there’s a min version (of PHP) required to run this? I have 5.1.6 and I’m getting: PHP Fatal error: Uncaught exception ‘ErrorException’ with message ‘setcookie() expects at most 6 parameters, 7 given’. Also, since I’m not too interested in the server side at this point is there a way I could point the JS (served from my server) to the REST API hosted on your system?
In case anyone runs into the issues I did… for this to work I had to have a version of PHP higher than 5.1 (5.3.3 worked for me when 5.1.6 did not). Apache has to be configured to AllowOverride FileInfo for the api directories, as this is necessary for .htaccess files to be processed which Slim.php depends on. On RHEL also had to have the php-mysql package installed. Thanks!
Thanks for the heads up Seth. Was facing this issue on the Twitter Bootstrap+Backbone tutorial and found your suggestions. Working fine now.
Like it and as other post indicated I really think there should be a version with CF back end.
regards
J
I think it is a bad idea to present an introductory tutorial like this and teach people to pollute the global namespace.
There are better ways of handling that situation.
Hello,
every time i use fetch() i need to wait for complete download of datas before doing templatinf for example.
You seem not need to wait for asynchronous call in your example:
this.wineList.fetch();
$(‘#sidebar’).html(this.wineListView.render().el);
how is it possible ?
thank you