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.
I can not get this application to show the data. If I navigate to the api url it returns the JSON data, but the front end is not connecting. I pulled over all of the source and attached it to mysql as instructed, but either the folder needs a different .htaccess connection or there is something I am missing. I saw your note about changing the .htaccess file. Do you have more detail about how you structured it. Which folder you put the rule into? Any help would be fantastic.
http://www.daddai.com/mie/cellar/
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
Thanks for the tutorial…One thing that I haven’t been able to figure out: Why do you have the model attribute for WineListView point to a collection (WineCollection), instead of setting the collection attribute to said collection of wines?
Nice post
Very helpful. :)
You can add a file names “wines” in folder api containing json response so that anyone can directly extract zip and run it.
Christophe…
I took a stab at rewriting your app using Ember.js. I’d love your feedback:
http://andymatthews.net/read/2012/03/22/Rewriting-Backbone.js-wineshop-demo-using-Ember.js
Thanks for your tutorial,
Can you tell me how you catch PHP errors ?
for exemple, imagine that the creation failed, you return json object but I think that header code return stay 200 so only success event will be fire.
so how do this ?
test response like that :
if( response.error ){ } ?
I think it’s interesting to wait for the server return before add (or update, or delete) item in the collection (like in Flex for exemple)
Thank you for your Tutorial. I’m still having a hard time getting your part1 sample to work. I try to put together the PHP sample with the Jersey sample (as the Jersey Sample isn’t using backbone.js). One thing I have discovered, as Jersey outputs the namespace “wine” that I had to use the parse method within the Collection:
window.WineCollection = Backbone.Collection.extend({
model:Wine,
url:”../winecellar/rest/wines”,
parse: function(data) {
return data.wines;
}
});
JSON Output:
{“wine”:[
{
“country”:”USA”,
“description”:”With hints of ginger and spice, this wine makes an excellent complement to light appetizer and dessert fare for a holiday gathering.”,
“grapes”:”Pinot Noir”,
“id”:”9″,
“name”:”BLOCK NINE”,
“picture”:”block_nine.jpg”,
“region”:”California”,”year”:”2009″
},
Now I don’t see any errors in Firebug, everything seems to be fine, JSON is parsed, but only the HTML part is displayed. All the templates seem not to be loaded. Without any errors that point me to the problem I’m feeling quite lost :-|
can not run this app in my place, not wines list was loaded
worked:
like Pawan Bhole said:
lost a file named ‘wines’ in api folder, the context is like http://coenraets.org/backbone-cellar/part1/api/wines
finally, found out, mac built-in apache REWRITE feature can not work correctly without additional configuring.
anyway , there is a fast way to let this app work: change the “url:”../api/wines” to url:”../api/index.php/wines” in main.js
how do we make our app available for people to download from the app store? I guess after the html and css files are ready we go to build.phonegap.com and create a build for different devices . where and how do i create the signing keys ofr android , ios ? Please your help will be greatly appreciates .. There might be so many people like me looking for the same information..
Hi,
The Wine Cellar application assumes that a single person is managing his wine cellar. Had this been a multi-tenant application where every user would login and manage his own cellar, how would we pass the session data to the REST service? Would we send the username and password along with every REST URL?
Thanks,
Yash
Trying to get this running on MAMP (OSX Lion) and strangely only the /bootstrap version included in the git repo seems to successfully connect to the DB and access the data. Can anyone suggest why the /tutorial versions seem unable to connect and access the wine data? It’s very frustrating to debug when a relative newby to php.
In reply to my own question… user: root had no privileges on the cellar database and my fix to this in mamp required going into phpMyAdmin, creating a user, assigning a password and then updating the database connection details at the bottom of /api/index.php
The bootstrap version of the app uses memorystore.js to create a local database for testing purposes and was displaying this local data instead of the database version, thus hiding the inability to connect to the DB.
Sorry for the posts but this might help some other newcomers to diagnose localhost issues.
Oh very good.
Thanks
I’ve never written any code before and would love to have access to someone that could create code for my affiliates that use my wysiwyg web app development dashboard. Would you be interested?
Very helpful and easy to read through and try out Backbone.js. Thanks for taking time and writing these posts.
Amazing tutorial ! I’ve been searching whole day for tutorial to get started using backbone.js and all others I managed to find were incomplete, bugged or using horrible coding. But this one is perfect. Thank you for putting your time in it and keep the good work!