Backbone.js Wine Cellar Tutorial — Part 2: CRUD

In Part 1 of this tutorial, we set up the basic infrastructure for the Wine Cellar application. The application so far is read-only: it allows you to retrieve a list of wines, and display the details of the wine you select.

In this second installment, we will add the ability to create, update, and delete (CRUD) wines.

RESTful Services

As mentioned in Part 1, Backbone.js provides a natural and 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.

This tutorial uses pure RESTful services. The services are implemented as follows:

HTTP Method URL Action
GET /api/wines Retrieve all wines
GET /api/wines/10 Retrieve wine with id == 10
POST /api/wines Add a new wine
PUT /api/wines/10 Update wine with id == 10
DELETE /api/wines/10 Delete wine with id == 10

A PHP version of these services (using the Slim framework) is available as part of the download. A similar Java version of the API (using JAX-RS) is available as part of this post.

Using Backbone.js with non-RESTful Services

If your persistence layer is not available through RESTful services, you can override Backbone.sync. From the documentation:

“Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server. By default, it uses (jQuery/Zepto).ajax to make a RESTful JSON request. You can override it in order to use a different persistence strategy, such as WebSockets, XML transport, or Local Storage.”

Using non-RESTful services is not discussed in this tutorial. See the documentation for more information.

Part 2: Adding Create, Update, Delete

You can run the application (Part 2) here. The create/update/delete features are disabled in this online version. Use the link at the bottom of this post to download a fully enabled version.

Here is the code for the improved version of the applications. Key changes are discussed below.

// Models
window.Wine = Backbone.Model.extend({

window.WineCollection = Backbone.Collection.extend({

// Views
window.WineListView = Backbone.View.extend({


    initialize:function () {
        this.model.bind("reset", this.render, this);
        var self = this;
        this.model.bind("add", function (wine) {
            $(self.el).append(new WineListItemView({model:wine}).render().el);

    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({



    initialize:function () {
        this.model.bind("change", this.render, this);
        this.model.bind("destroy", this.close, this);

    render:function (eventName) {
        return this;

    close:function () {

window.WineView = Backbone.View.extend({


    initialize:function () {
        this.model.bind("change", this.render, this);

    render:function (eventName) {
        return this;

        "change input":"change",
        "click .save":"saveWine",
        "click .delete":"deleteWine"

    change:function (event) {
        var target =;
        console.log('changing ' + + ' from: ' + target.defaultValue + ' to: ' + target.value);
        // You could change your model on the spot, like this:
        // var change = {};
        // change[] = target.value;
        // this.model.set(change);

    saveWine:function () {
        if (this.model.isNew()) {
        } else {
        return false;

    deleteWine:function () {
            success:function () {
                alert('Wine deleted successfully');
        return false;

    close:function () {

window.HeaderView = Backbone.View.extend({


    initialize:function () {

    render:function (eventName) {
        return this;

        "click .new":"newWine"

    newWine:function (event) {
        if (app.wineView) app.wineView.close();
        app.wineView = new WineView({model:new Wine()});
        return false;

// Router
var AppRouter = Backbone.Router.extend({


    initialize:function () {
        $('#header').html(new HeaderView().render().el);

    list:function () {
        this.wineList = new WineCollection();
        this.wineListView = new WineListView({model:this.wineList});

    wineDetails:function (id) { = this.wineList.get(id);
        if (app.wineView) app.wineView.close();
        this.wineView = new WineView({});


var app = new AppRouter();


Two attributes were added to the Wine Model:

  • urlRoot: RESTful service endpoint to retrieve or persist Model data. Note that this attribute is only needed when retrieving/persisting Models that are not part of a Collection. If the Model is part of a Collection, the url attribute defined in the Collection is enough for Backbone.js to know how to retrieve, update, or delete data using your RESTful API.
  • defaults: Default values used when a new instance of the model is created. This attribute is optional. However, it was required in this application for the wine-details template to render an ’empty’ wine model object (which happens when adding a new wine).


When a new wine is added, you want it to automatically appear in the list. To make that happen, you bind the View to the add event of the WineListView model (which is the collection of wines). When that event is fired, a new instance of WineListItemView is created and added to the list.


When a wine is changed, you want the corresponding WineListItemView to re-render automatically to reflect the change. To make that happen, you bind the View to the change event of its model, and execute the render function when the event is fired.

Similarly, when a wine is deleted, you want the list item to be removed automatically. To make that happen, you bind the view to the destroy event of its model and execute our custom close function when the event is fired. To avoid memory leaks and events firing multiple times, it is important to unbind the event listeners before removing the list item from the DOM.

Note that in either case we don’t have the overhead of re-rendering the entire list: we only re-render or remove the list item affected by the change.


In the spirit of encapsulation, the event handlers for the Save and Delete buttons are defined inside WineView, as opposed to defining them as free-hanging code blocks outside the “class” definitions. You use the Backbone.js Events syntax which uses jQuery delegate mechanism behind the scenes.

There are always different approaches to update the model based on user input in a form:

  • “Real time” approach: you use the change handler to update the model as changes are made in the form. This is in essence bi-directional data binding: the model and the UI controls are always in sync. Using this approach, you can then choose between sending changes to the server in real time (implicit save), or wait until the user clicks a Save button (explicit save). The first option can be chatty and unpractical when there are cross-field validation rules. The second option may require you to undo model changes if the user navigates to another item without clicking Save.
  • “Delayed” approach: You wait until the user clicks Save to update the model based on the new values in UI controls, and then send the changes to the server.

This discussion is not specific to Backbone.js and is therefore beyond the scope of this post. For simplicity, I used the delayed approach here. However I still wired the change event, and use it to log changes to the console. I found this very useful when debugging the application, and particularly to make sure I had cleaned up my bindings (see close function): I you see the change event firing multiple times, you probably didn’t clean up as appropriate.


Backbone.js Views are typically used to render domain models (as done in WineListView, WineListItemView, and Wine View). But they can also be used to create composite UI components. For example, in this application, we define a Header View (a toolbar) that could be made of different components and that encapsulates its own logic.


The source code for this application is hosted on GitHub here (see part2). 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.

What’s Next?

The application so far doesn’t support deep-linking. For example, select a wine in the list, grab the URL in the address bar and paste it in another browser window: it doesn’t work. In Part 3, we will add complete support for deep linking.

73 Responses to Backbone.js Wine Cellar Tutorial — Part 2: CRUD

  1. Playeren February 17, 2012 at 4:11 pm #


    Thanks for the great turorial! A quick n00b question:
    I can’t figure out where you’re setting the id for the new wine you’re creating – what am I missing?

    • Playeren February 17, 2012 at 4:53 pm #

      Never mind :) I looked at the API and sql and figured it out.

      • Max Krog June 7, 2014 at 12:10 pm #

        I got the same question but can’t find an answer my self. Can anyone explain how the ID for every model is set? I have figured putt that _id and id are related in some way.

        • Justin August 8, 2014 at 1:21 pm #

          The id for new models is set by the server side i.e. when backbone does a POST to the right URL, the backend components will take care of persisting the new entity. The database returns the id of the newly created row and the server side sends back this id so your front end Javascript objects (the models) get their ids populated.

          I haven’t looked into how the format is expected to be (as in what this: { “id”: 1 } or something? Or even how to tweak the HTTP requests .. for instance you want pagination with the GET and not **all** the wines in the db for instance… but any way, I’m guessing there should be a way to do that… but yes, regarding your question… even though I didn’t look it up that is the only sensible answer.

  2. Lucas April 2, 2012 at 8:48 pm #

    I’ve found small error in your application. If you open this page – you will get this error:

    TypeError: ‘undefined’ is not an object (evaluating ‘this.wineList.get’)

    I think you should initialize wineList variable in the initialize phase of AppRouter instead of the ‘list’ method. Above link take you directly to wineDetails action so there is no change to create wineList object.

    PS. Thank you for your tutorial, it helps me to better understand backbone.

    • Lucas April 2, 2012 at 8:51 pm #

      I’m sorry I haven’t noticed you fixed this issue in Part 3 of your tutorial. You can ignore my previous comment :).

  3. Ricardo April 8, 2012 at 9:55 pm #

    Great tutorial, I’m loving this so far. Hadn’t yet found a good resource that both uses Backbone and a simple RESTful API, and this just about covers my basic needs. Would love a fourth part where you’d explore more on the underscore.js methods ;)

    My only doubt goes beyond the scope of all this, however. Why write this application entirely on a global scope? Encapsulating the code on an anonymous function would be a better practice, any reason why not in this particular case?

    Thanks for taking the time to do this, hugely appreciated.

    • Levi May 23, 2012 at 9:26 pm #

      Im assuming he did it this way to save time and maintain focus on backbone.js, not delving on JS best practices.

  4. DK April 17, 2012 at 2:37 pm #

    had to tweak this line in Models.js to the below on OS X with built in PHP and Apache combo
    must be missing a setting somewhere, but this was a faster route to get’r going


  5. Totti Anh Nguyen May 25, 2012 at 2:11 am #

    Thanks for a very helpful tutorial :). Btw, minor fix, this line
    this.wineListView = new WineListView({model:this.wineList});

    should have been :
    this.wineListView = new WineListView({collection:this.wineList}); // more proper use of @property collection.

  6. Dirk-Jan de Groot July 16, 2012 at 4:09 pm #

    This tutorial is very good work. Thanks!

    FYI: there is a line in your /tutorial/api/index.php that was causing a conflict on my XAMPP installation and at least one other user experienced the same problem as you can read here:

    It looks like a bug to me that this is the first line of the function addWine:
    error_log(‘addWine\n’, 3, ‘/var/tmp/php.log’);

    That line was causing the issue since there (as far as i know) is no /var/tmp directory on my windows pc.

    The issue resulted in receiving 500 application errors for all POST requests.

  7. jbcavarec July 18, 2012 at 10:29 am #

    Hello, thank you for your amazing tutorial
    I am testing part by part “Wine cellar” , i encounter some little problem i have in my own application .
    if i click on one item i.e /#wines/11 , ok my right list print details … if i reload page (with url /#wines/11 the left list disappear and i have message “this.winelist undefined” in firebug . How can i change url ?

  8. jbcavarec July 18, 2012 at 10:57 am #

    Please , i have another question,
    How can you be sure wineList is fetched before make a render , this 2 instructions are asynchronous , i en countered this pb too in my app .

    this.wineList.fetch() ;
    $(‘#sidebar’).html(this.wineListView.render().el) ;


  9. Chet July 20, 2012 at 12:48 am #

    This has easily been one of the best backbone tutorials for learning the framework. Integrated the ideas and principles shown here to very easily to replace an old ASP.NET application with some HTML5, JavaScript, JQuery Mobile goodness.

  10. Chris Lee January 4, 2013 at 9:45 am #

    why set urlRoot as “../api/wines”?

    • Chris Lee January 4, 2013 at 9:59 am #

      ooh, you wrote this article earlier than the nodejs express mongodb one, so the back-end part is quite differrent!

  11. Maddy January 14, 2013 at 2:50 pm #

    I having one problem with backbone-jax-cellar applicatoin.
    I followed all the step which are given in read me file.
    i am using eclipse for run this application while running this application with Run configuration its giving error like main not found….so what is the solution?how to run this application?

  12. dongming April 23, 2013 at 11:39 am #

    when I refreshed the url which like ‘/part2/#wines/20’ directly,I got an error in the console of chrome

  13. Spectacular conquer! I have to beginner at the same time while you fix your internet site, how may possibly i sign up for a site web page? Your account made it easier for me a acceptable offer. I have been somewhat acquainted of your ones transmit provided lively see-through thought

  14. May 23, 2014 at 12:50 am #

    Simply just stumbled onto your blog below. Large amount of good details My partner
    and i had been considering. Many thanks regarding taking a few minutes to
    talk about.
    Great blog you’ve taking place below. A lot of details becoming contributed.
    Very much loved.
    You then have a awesome weblog. Carry on the excellent work.
    Enjoy your own composing.
    Fantastic details you might have right here. Continue
    That is a few extremely great stuff you happen to be writing.
    Who have got idea.
    Incredibly fascinating info here. You’ve got a wide range of information within this subject definitely.

    This is many awesome things here. Brilliant facts. Keep it

  15. top mens cologne May 23, 2014 at 8:30 pm #

    Thanks for some other informative blog. Where else could I am getting that kind of information written in such a perfect means?
    I’ve a mission that I’m just now running on, and I’ve been on
    the glance out for such information.

  16. Krystle May 24, 2014 at 4:22 pm #

    Affiliate Marketing Domain Name Gdi Free Affiliate Program
    What else could we possibly need. From customer satisfaction to the importance of certain aspects of your company, this is a very versatile option to use in your
    online marketing questionnaires. Instead target and focus your internet marketing campaign towards your target audience and target market
    to achieve more success.

  17. free amazon gift card code generator May 26, 2014 at 7:39 am #

    Admiring the time and energy you put into your site and in depth information you present.

    It’s good to come across a blog every once in a while that isn’t the same unwanted rehashed information.
    Great read! I’ve saved your site and I’m including your RSS feeds to my
    Google account.

  18. cliquez ici May 30, 2014 at 3:02 pm #

    I’m extremely impressed with your writing skills
    as well as with the layout on your blog. Is this a paid theme or did you customize it yourself?
    Anyway keep up the nice quality writing, it’s rare to see a great blog like this one nowadays.

    – cliquez ici
    – cliquez ici
    – cliquez ici
    – cliquez ici
    – cliquez ici
    – cliquez ici

  19. best perfume for men June 4, 2014 at 2:04 am #

    I don’t even know how I ended up here, but I
    thought this post was good. I do not know who you are but definitely
    you are going to a famous blogger if you aren’t already
    ;) Cheers!

  20. ทัวร์ฮ่องกง June 9, 2014 at 3:07 pm #

    Hey there! This is my first visit to your blog!

    We are a collection of volunteers and starting a new project in a community in the same niche.
    Your blog provided us useful information to work on. You have done a
    extraordinary job!

  21. view this page June 9, 2014 at 10:52 pm #

    I have been exploring for a little for any high quality articles or weblog posts in this sort of area .
    Exploring in Yahoo I ultimately stumbled upon this website.
    Reading this info So i am glad to express that I have an incredibly just right uncanny feeling I came upon exactly what I needed.
    I such a lot definitely will make sure to don?t put out of your mind this web site
    and provides it a look on a continuing basis.

  22. ขายบ้านระยอง June 11, 2014 at 1:48 am #

    My programmer is trying to persuade me to move to .net from PHP.
    I have always disliked the idea because of the
    costs. But he’s tryiong none the less. I’ve been using WordPress on a number of websites for about a year and am anxious about switching to another
    platform. I have heard excellent things about
    Is there a way I can transfer all my wordpress content into it?
    Any help would be greatly appreciated!

  23. งานคีย์ข้อมูล June 26, 2014 at 11:49 am #

    Hi there to every body, it’s my first pay a visit of this web site; this weblog contains
    awesome and truly excellent stuff for visitors.

  24. çelik raf April 27, 2015 at 6:13 pm #

    Encapsulating the code on an anonymous

  25. futbol çorapları September 10, 2015 at 4:41 pm #

    Read the comments! Someone has already encountered and solved

  26. çelik raf October 15, 2015 at 8:39 am #

    working yet because I’m using Hibernate in the mid-tier

  27. forma imalatı November 16, 2015 at 3:54 am #

    Все качества продукции

  28. firma ekle February 3, 2016 at 6:31 pm #

    başarınızı arttıracak firma sitesi

  29. çelik raf March 28, 2016 at 6:20 am #

    great news, ı m like your work Mister thank you

  30. Lorenzo Jiménez September 22, 2016 at 7:11 pm #

    This example dos not work for the latest version of backbone on 2016.

  31. Oktay Seo April 12, 2017 at 2:47 pm #

    Kiralık kaftan ile en özel gününüzü ışıl ışıl aydınlatabilirsiniz.
    Geçmişi yüzlerce yıl öncesine kadar uzanan kına; eski Mısır, Roma ve Ortaçağ’da boya ve ilaç olarak kullanılmıştır. Eski Türkler kınayı, veba hastalığına karşı şifa ve doğal boya maddesi olarak kullanmıştır. Türk-İslam geleneğinde; hem sağlık, hem güzellik, hem de törensel açıdan özel bir yeri olan ve Dede Korkut hikayelerinde de sözü edilen kına, Türk inançlarına göre adanmış olmanın bir işaretidir.
    Kına’nın hem kültürümüzde önemli bir yer edinmesi hemde faydaları sıralanamaz. Kına daha çok düğünden önce yakılır .
    Bağdat kına organizasyonu olarak ihtiyaçlarınızı karşılamayı hedef edinmekteyiz.
    Ayrıca kiralık kaftan modellerimizi incelemenizi tavsiye ederiz.

  32. ABDQUDUS DAMILARE May 13, 2018 at 7:30 pm #

    thank You for this. Great articles i appreciate this from Youon Wine cellar.

  33. Godwin Okon May 21, 2018 at 5:00 am #

    Am actually Gonna Try This Once I get Home..You CAN Check me out at Download Latest Nigerian Naija Music

  34. Etz Jayprinz Ajiboye Jpdesign June 27, 2018 at 5:30 pm #

    ust gonna try thsi though
    Download Latest Nigerian Naija Music

  35. Anneww September 8, 2018 at 4:50 am #

    Coding made easy

  36. Oladejo Joshua Jay December 3, 2018 at 10:53 am #

    i will Give It a trial Download Latest Music and Video Here

  37. Oladejo Joshua Jay December 3, 2018 at 10:55 am #

    Nice article: Latest Music Download Here

  38. Oladejo Joshua Jay December 25, 2018 at 11:28 am #

    Best Tips

    South Africa Hip-Hop Mp3 Download, Albums Zip, Mixtapes & Videos

  39. Felix Goody January 5, 2019 at 5:04 am #

    Am definitely gonna try this out. Very nice article. Thanks Wine Celler

  40. Rhianne Jane August 24, 2019 at 7:50 am #

    I amazed with the research you made to create this actual put up amazing.
    Great activity!

  41. Examrated Admin October 4, 2019 at 3:01 pm #

    Nairaflaver Music

  42. Felix Goody December 27, 2019 at 10:23 am #

    Thanks for sharing 😇😇This is Awesome! andGreat!

  43. Gray123 April 22, 2020 at 9:08 pm #

    Visit for your music download from all south african artists

  44. Samuelsss May 6, 2020 at 6:18 pm #

    Been Looking for this for ages. The backbone Js Wine cellular. Glad i found this. Do well to check out the Extraction movie subtitle now and have fun while you do so.

  45. yuvaraj singh December 9, 2020 at 2:26 am #

    Thanks for sharing this informative content
    Leanpitch provides online training in Product Management Launchpad during this lockdown period everyone can use it wisely.
    Product Management Workshop

  46. Examrated Admin March 4, 2021 at 3:25 am #

    I’m a lover of Fzmovies and a big fan of Adekunle Gold Mp3 Albums and i also love reading Nigerian Celebrities Biography and as well love listening Chike Download Mp3 and Song Lyrics and Joeboy Mp3 Download


  1. Confluence: Mobile Devices Development - December 26, 2011



  2. Backbone.js Wine Cellar Tutorial — Part 1: Getting Started - January 5, 2012

    […] Part 2 is available here. […]

  3. Backbone.js Wine Cellar Tutorial — Part 3: Deep Linking and Application States - January 11, 2012

    […] Part 1 of this tutorial, we set up the basic infrastructure for the Wine Cellar application. In Part 2, we added the ability to create, update, and delete (CRUD) […]

  4. Backbone.js es sexo | Asier Marqués - January 26, 2012

    […] a través de un objeto json, que normalmente incluirá el Modelo. El modelo se puede integrar muy fácilmente con nuestra API de servicios en el backend, permitiéndonos hacer un CRUD desde el cliente de forma […]

  5. Backbone.js Lessons Learned and Improved Sample App - January 31, 2012

    […] few weeks ago, I posted a three-part Backbone.js tutorial (part 1, part 2, part 3). Since then, I spent more time building a real-life application with Backbone. I ran into […]

  6. Sample Application with Angular.JS - February 2, 2012

    […] I blogged a three-part Backbone.js tutorial (part 1, part 2, part 3), a number of people asked me to try Angular.js. So I decided to take it for a test drive. […]

  7. Sample Mobile App with Backbone.js and PhoneGap - February 7, 2012

    […] recently blogged a tutorial (part 1, part 2, part 3, and postface) that takes you through the process of building a CRUD application using HTML […]

  8. Learn Backbone.js Completely | JavaScript is Sexy - January 14, 2013

    […] these two tutorials: — 
Backbone.js Wine Cellar Tutorial — Part 2: CRUD 

Backbone.js Lessons Learned and Improved Sample App
 (This is a MUST […]

  9. Lexicons and RESTful APIs | Doing Web Linguistics - January 25, 2013

    […] to have URLs that look more or less like this (based on a table from a nice tutorial called “Backbone.js Wine Cellar Tutorial — Part 2: CRUD by Christophe […]

  10. Developing Java web applications with Backbone.js - April 26, 2013

    […] have learned the where-about from Backbone.js Wine Cellar Tutorial — Part 2: CRUD by Christophe Coenraets and used his sample POC to develop a J2ee web application with […]

  11. Sample App with Backbone.js and Twitter Bootstrap | HTML CSS3 - June 28, 2013

    […] you are new to Backbone.js, you may want to start with the tutorial (part 1, part 2, part 3, and postface) I blogged recently. “Backbone Directory” includes some interesting […]

  12. backbone.js | mauroprogram's Blog - February 18, 2015

    […] […]

  13. javascript - uncaught TypeError: No se puede llamar método 'reemplazar' undefined backbone.js - November 23, 2018

    […] una sencilla aplicación a través de RSS backbone.js. I ‘m usando este backbone.js tutorial. I ‘m obteniendo el siguiente error, en la línea 2(plantilla), cuando la definición de la […]

  14. Backbone 介绍及学习资料索引 | Hugo Web前端开发 - December 29, 2018

    […] Wine Cellar Tutorial    这个酒窖例子相当不错,非常完整,包含了服务器端。一共分了3篇文章来介绍,part2、part3 […]

  15. javascript - uncaught TypeError: Ne peut pas appeler la méthode 'remplacer' undefined backbone.js - January 12, 2019

    […] développer un RSS simple application à l'aide de backbone.js. Je m 'l'aide de cette backbone.js tutoriel. Je m 'obtenir l'erreur suivante, sur la ligne 2(modèle), lors de la définition du modèle. […]

Leave a Reply