Building a Scalable App With Backbone.js

Building a Scalable App With Backbone.js

Tutorial Details
  • Diffulcty: Intermediate
  • Completion Time: 1 Hour

Backbone.js is a small library (~5kb minified) that allows you to build single page web applications. Unlike many of its peers, Backbone is not very opinionated about the way you use it. Aside from some basic concepts, the design of your application is left widely up to you.

This tutorial will offer some insight on one of the popular patterns that the community has started to embrace: the Backbone Boilerplate. We will use this boilerplate to create a simple library of books, which you could easily extend into a much more robust application.


A Quick Overview of Libraries

It’s very flexible and incredibly lightweight.

Backbone.js is a JavaScript framework that allows us to easily create single page web applications. It’s very flexible and incredibly lightweight, which is why it has become one of the most popular JavaScript frameworks available.

Require.js is a module loader (leveraging the AMD design pattern) that allows you to asynchronously load your JavaScript modules and their dependencies.

Underscore.js a library that provides a set of utility functions you would come to expect when working with a programming language. Among other things, it gives you the ability to iterate over collections, to test if code is a function, and has a templating language built-in.


What is Backbone Boilerplate?

The Backbone Boilerplate is simply a set of best practices and utilities for building Backbone web applications. It is not an additional library, but it merges together a few libraries to encourage some structure when creating Backbone projects.

The Backbone Boilerplate is not an additional library.

There are a couple of ways to install the Backbone Boilerplate. The easiest (and preferred) method is the grunt-bbb plugin. However, that requires the use of Node.js and NPM, which is out of the scope of this tutorial. We will be doing a manual install instead.

To get started, head on over to the Github repository and download a copy of the code (you should see the .zip icon near the top). The copy you are downloading has been modified from the original with a lot the example code removed. There are a bunch of very useful comments in the example code (from the original boilerplate) – feel free to read over them in your spare time.

That’s it! We can get starting with creating our application.


Your First Module, the Book!

When you are working with the Backbone Boilerplate (or any project using AMD/Require.js), you will be grouping functionality into modules, and generally putting each module in its own file. This creates a “separation of concerns” and allows you (and anyone else who reads your code) to easily understand what the code should be doing.

To create your first module, simply put the following code into the file app/modules/book.js.

define([
  "namespace",
  "use!backbone"
],

function(namespace, Backbone) {

  var Book = namespace.module();

  // Router
  Book.Router = Backbone.Router.extend({
    routes: {
      "book/:p"   : "details"
    },

    details: function(hash){
      var view = new Book.Views.Details({model: Library.get(hash)});
      view.render(function(el){
        $("#main").html(el);
      });
    }
  });

  // Instantiate Router
  var router = new Book.Router();

  // Book Model
  Book.Model = Backbone.Model.extend({});

  // Book Collection
  Book.Collection = Backbone.Collection.extend({
    model: Book.Model
  });  

  // This will fetch the book template and render it.
  Book.Views.Details = Backbone.View.extend({
    template: "app/templates/books/details.html",

    render: function(done) {
      var view = this;

      // Fetch the template, render it to the View element and call done.
      namespace.fetchTemplate(this.template, function(tmpl) {
        view.el.innerHTML = tmpl(view.model.toJSON());

        if (_.isFunction(done)) {
          done(view.el);
        }
      });
    }
  });

  // This will fetch the book list template and render it.
  Book.Views.List = Backbone.View.extend({
    template: "app/templates/books/list.html",

    render: function(done){
      var view = this;

      namespace.fetchTemplate(this.template, function(tmpl){
        view.el.innerHTML = tmpl({books: view.collection.toJSON()});

        if (_.isFunction(done)){
          done(view.el);
        }
      });
    }
  });

  // Required, return the module for AMD compliance
  return Book;

});

This might look like a lot, but it is really quite simple. Let’s break it down below:

The AMD Module Definition

define([
  "namespace",
  "use!backbone"
], function(namespace, Backbone){
  var Book = namespace.module();

  return Book;
});

This is the standard format for any AMD module definition. You are telling the module loader that this module needs access to your namespace and backbone, which are defined in app/config.js. Inside the callback function, you are registering your module, and returning it at the end (which follows AMD compliance).

The Module’s Router

Book.Router = Backbone.Router.extend({});
var router = new Book.Router();

Whenever the browser is directed to a route in the routes hash, the associated function is called. This is usually where you instantiate the view and call its render function. We instantiate the router so Backbone knows to start picking up the associated routes.

The Module’s Data

Book.Model = Backbone.Model.extend({});
Book.Collection = Backbone.Collection.extend({
  model: Book.Model
});

This is where your book data and business logic is defined. You will create new instances of your Book.Model to store each book and its attributes (title, author, etc). Book.Collection is associated with Book.Model, and it is how you represent your models as grouped entities. In other words, a library has many books, and a collection is a lot like a library.

These are pretty bare, but you can place any of your business logic in the objects that are passed to the extend methods. If, for instance, you wanted to create a function that would filter books from the collection based on the author, you would do something like the following:

Book.Collection = Backbone.Collection.extend({
  model: Book.Model,

  filterByAuthor: function(author){
    return this.filter(function(book){
      return book.get('author') === author;
    });
  }
});

“Underscore functions can be called directly on a Backbone collection.”

This is leveraging the Underscore filter function, which (like most of the Underscore functions) can be called directly on the collection itself. Feel free to read the Backbone documentation for more information on what Underscore functions you can call on your collections.

The same idea applies to your models. You should ideally push all of your business logic to the model. This might be something like adding the ability for your users to set up a book as a ‘favorite.’ For now, you can remove the filterByAuthor method from your collection, as we won’t be using that in this tutorial.

The Module’s Views

Book.Views.Details = Backbone.View.extend({
  template: "app/templates/books/details.html",

  render: function(done) {
    var view = this;

    // Fetch the template, render it to the View element and call done.
    namespace.fetchTemplate(this.template, function(tmpl) {
      view.el.innerHTML = tmpl(view.model.toJSON());

      if (_.isFunction(done)) {
        done(view.el);
      }
    });
  }
});

Your module will contain multiple views. In our example, we have a list view and a details view. Each of these has its own template, and a render function which calls fetchTemplate (defined in namespace.js), sets the result to the views innerHTML, and calls the associated callback function (done). One thing to notice, the list view is passing a collection to its template function, while the details view is passing the model to its template function. In both cases, we are calling toJSON() on the parameter. This helps us ensure that we are simply dealing with data at the template level.


Templates, With Little to No Logic

In app/templates/books/list.html

<h1>Listing of Books</h1>

<ul>
  <% _.each(books, function(book){ %>
    <li><a href="book/<%= book.id %>"><%= book.title %></a></li>
  <% }); %>
</ul>

In app/templates/books/details.html

<h1><%= title %></h1>

<ul>
  <li><b>Author: </b><%= author %></li>
  <li><b>Year Published: </b><%= published %></li>
</ul>

<a href="/">Back to List</a>

Since we have a details view and a list view, we will need a template for each of them. In the list view, we will iterate over our collection and render a link to each book’s details view. In our details view, we display individual pieces of data pertaining to the book that was clicked. We are able to use the properties directly because we are passing the data into the template function with their toJSON() methods, which converts standard models/collections to their JSON representations.

Notice the fact that we didn’t have to call preventDefault() for any of the links that were on the page? That is because of the code at the bottom of app/main.js. We are saying any link on the page without data-bypass="true" will automatically invoke preventDefault(), using our Backbone routes instead of default link behaviour.


Bootstrapping Your Data and Setting Your Default Route

At the top of main.js, replace the code with the following:

require([
  "namespace",

  // Libs
  "jquery",
  "use!backbone",

  // Modules
  "modules/book"
],

function(namespace, $, Backbone, Book) {
  window.Library = new Book.Collection([
    { id: 1, title: "A Tale of Two Cities", author: "Charles Dickens", published: 1859 },
    { id: 2, title: "The Lord of the Rings", author: "J. R. R. Tolkien", published: 1954 },
    { id: 3, title: "The Hobbit", author: "J. R. R. Tolkien", published: 1937 },
    { id: 4, title: "And Then There Were None", author: "Agatha Christie", published: 1939 }
  ]);

  // Defining the application router, you can attach sub routers here.
  var Router = Backbone.Router.extend({
    routes: {
      "":   "index"
    },

    index: function(){
      var view = new Book.Views.List({collection: Library});
      view.render(function(el){
        $("#main").html(el);
      })
    }
  });

  // Everything after the Router stays the same
});

Typically, your server side component would pass data to your Backbone application through its API (you set these up in your Collections and Models). However, in this tutorial we are simply bootstrapping your views with a few static books, and creating the default route, which passes the Library to the Book list view as its collection.

The only other thing that has to change is the fact that you are passing your module into the module loader (notice the require instead of the define at the top of main.js). By doing this, you are telling your application to load the file and passing it into the callback function so you have access to all of the Book properties.


Wrapping Up

There are a number of other pieces to truly having a scalable web application.

You might be thinking that this looks very similar to every other Backbone tutorial you have read, so what makes this one different? Well, the key to this is the fact that all functionality relating to the Book is stored in one module, which is in its own file. Let’s say you decided to start sharing your movie collection on this site as well. It would be as simple as creating app/modules/movie.js, the associated templates, and telling main.js to load modules/movie.

There are a number of other pieces to truly having a scalable web application, the biggest of which is a robust API on the server. However, if you remember to create a separation of concerns when dealing with different modules in your application, you will find it much easier to maintain, optimize, and grow your code without running into too many issues as a result of unruly spaghetti code.

Additional Backbone learning on Nettuts+.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.antidote-design.net randy

    The download link is linking to a .zip.xml.

    By the way, thanks so much for using the Backbone Boilerplate in the tut.

    • http://www.antidote-design.net randy

      Maybe its an empty zip.

  • http://tbranyen.com/ Tim Branyen

    Thanks for writing about the Backbone Boilerplate! One suggestion might be to add a paragraph on the migration of use.js to RequireJS 2.0 shim.

    http://requirejs.org/docs/api.html#config-shim

  • http://codeforest.net Codeforest

    Nice little intro to Backbone. The only problem is that there is a bunch of articles like this and very few about server side of the mix.

    Thanks

  • Mike

    “To create your first module, simply put the following code into the file app/modules/book.js.”

    There doesn’t appear to be that file in the download – are we to create it ourselves?

  • Valentin Cabourdin

    What do you think about splitting your module into different files in order to separate collections, models and views ?

    How do you manage a module that has a lot of collections, models, views ?

    All in one file ?

    thanks

    • Jesus Bejarano

      Normaly you should have each instances in separate folders :
      js/model/app.js,
      js/conllection/app.js,
      js/view/app.js,

      But since this was a short code , i guess he tough that the one file aproach is fine for a example . Why don’t you try yeoman it will generate a scaffold of all folders structure that you need to work with backbone nicely.

      • Valentin Cabourdin

        Ok thanks, i’ll try yeoman. Hope that it works fine with marionette

    • http://tbranyen.com/ Tim Branyen

      I find small modules in single files works most of the time. In my applications, however, I’ve found that breaking into separate files for just Views or just Model/Collection works really well.

      Creating a separate file for every single class/constructor seems really tedious.

  • John

    I can’t download the tutorial source files, can you please check the download link?

  • David

    I dont think the download works. It keeps giving me an error that it cant find the zip

  • http://yemaw.me Ye Maw

    Thanks for the article. But the download link doesn’t work.

  • McLenithan

    Nice work! Exactly what I needed help with right now. Thanks a bunch!

  • http://Gyanmoti.in Nirmal Kumar

    Good Explanation. Can someone explain why we using “use!backbone” in the require.js? What this represents?

    Thanks
    Nirmal

  • Slobodan

    I have a problem loading namespace.js and use.js following this tutorial. I would download the code if the download wasn’t broken.
    Can you please fix this? 4 times out of 10 the demo link or the download is broken on this website…

  • http://www.joezimjs.com Joe Zim

    Why is an article titled “Building a Scalable App With Backbone.js” not about building a scalable app? It’s moreso an introduction to Backbone Boilerplate and a horrible way of building with Backbone that isn’t very scalable.

    • http://tbranyen.com/ Tim Branyen

      I’m not sure you’ve looked at the Backbone Boilerplate, but its not full of any surprises… just Backbone and some third party open source libraries.

      I’m interested in why you think the above approach is a “horrible” way of building. Such a loaded term must have some good stuff backing it. My goals are to make Backbone Boilerplate the best so any insight is appreciated!

  • ian

    I do appreciate your explanation of modules but …

    I too am wondering about the ‘use!backbone’ thing. Unless I’m missing something here ‘backbone’ would work just fine.

    Also the addition of the namespace.js file without a walkthrough is confusing when a newbie like myself looks at the original boilerplate. I wish you would have started with the original and explained additional files and modifications to arrive at the modified version you provide for the tutorial.

    I’m not at a loss on this tutorial because I’ve struggled through most if not all of the other backbone tutorials (some good and some bad) here and other places on the internet but I think I would be lost or at least confused if I was a complete newbie.

    • Ian

      Ah, never mind about the ‘use!’ part. I see Tim’s comment above. Thanks Tim.

  • Errorson

    XML from the download source link:

    AccessDenied
    Access Denied
    C0365448ABAF9397

    lkLtSJ1l7YbyLsRrET5Z7ifLeonx5S8Km8/eaiffiWwdQnG7lzZawGA2iyqvPMnL

  • http://zsitro.com Zsitro

    The title says “scalable”. The source code here is not well structured to achieve this goal.

    e.g.: views render() method in router file? :) funny

    Agree with the prev comments: there are tons of similar tutorials out there.

    It is a great example how to combine requirejs and backbone though.

  • Bob

    It would be helpful to have some vertical white space separating code blocks in the examples.
    Thanks for the tutorial!

  • http://tbranyen.com/ Tim Branyen

    What happened to all the comments?

    • JeremyMcPeak

      They are being imported. It can take about 24 hours before they’re back.

      • http://tbranyen.com/ Tim Branyen

        Rock on, glad you switched to Disqus =)

  • http://twitter.com/federustic Federico Commisso

    Awesome tut

  • http://www.facebook.com/people/Ricky-Duckworth/1533079619 Ricky Duckworth

    I’d really like a similar tutorial for ember.js. Nonetheless great intro tut.

  • mfiers

    Great information!
    I cannot download the demo.zip file however. It says access denied. Someone knows how this comes?

    • johndurbinn

      ” Someone knows how this comes?”

      That’s a pretty awkwardly phrased sentence.

  • Gareth

    The download link is not working

  • http://www.andrewthorp.com Andrew Thorp
    Author

    Thanks for the kind words guys! And thanks to @tbranyen:disqus for making the original boilerplate. I hope this helps!

  • Darren McPherson

    I only scanned the code examples, but they look a lot like the code examples from AAron Hardy’s JS Architecture series, last year:

    http://aaronhardy.com/javascript/javascript-architecture-the-basics/

  • http://twitter.com/firatcali darth

    This is such a mess. What happened to server side development?

    • asSa

      lado kha ma chikne

    • Null Pointer Expert

      A large asteroid came and made it nearly extinct. Given enough time, it will go extinct and furthermore evolve into, birdlike creatures or somesuch. But really, clustering and scalability is greatly enhanced when the server doesn’t have to keep track of user sessions, which is required for server side development. This is the next natural step with the onset of cloud computing (I.E. dynamic VPS solutions, where scaling is near automatic).

      This “mess” will get cleaner as standards and frameworks evolve and mature. This is the natural process of progress.

    • johndurbinn

      Servers aren’t webscale

  • http://www.facebook.com/chris.m.374 Chris Mulson

    thanks for the article, i’m not sure this addressed the best practices for building a scalable app as much as it is an introduction to backbone boilerplate and require.js.

    syntax like “use!backbone” makes me want to stick a fork into my eyes. this is completely unnecessary as you can load backbone with require.js by itself in a much cleaner and readable way

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • sdsadsa

    lado kha ma chikne

  • Thomas Zaleski

    Thanks for your article. A few things:

    It looks like the code in the GitHub repo is 6 months old. RequireJS is currently at version 2.0.4 and Underscore is at version 1.4.0. The Bootstrap used in your article is using RequireJS version 1.0.8 and Underscore version 1.3.3.

    The reason I bring this up is because I found other articles that use shim in the require.config to configure Backbone and Underscore dependencies to ensure they load properly which suggests that the use.js plugin is no longer necessary. It would be nice if you expanded a bit on that. Why did you decide to use the plugin versus just using the shim configuration?

    Also, your code uses one model and one collection. If you are writing a tutorial about scalable backbone applications it would be nice if you actually … well.. scale it by using additional routes, models and collections in your code. As a bonus it would be great if you touched on how your code prevents javascript memory leaks as it scales. I specifically refer to unbinding events and resources when navigating between views. A lot of examples out there that use one model and one collection will run into trouble when scaled because of potential memory leaks. After all, scalable applications need to take that into consideration.

    An explanation as to why you use the namespace.js would be great too. Why is this approach better when building scalable applications?

    I don’t mean to be harsh but so far the title of this tutorial is misleading and in my case a waste of time.

  • http://twitter.com/rei_liit John Kevin Basco

    Thank you very much for this tutorial! Very useful!

  • mohamed gaber

    Hello Andrew, Thank you for your great tutorial, I’want to tell you you helps me to create my first Backbone app.

    I’d like to ask if you may update your repo. to be compatible with the original project.

    Many thanks :)

  • http://www.habdas.org/ Josh Habdas

    Here’s how to get your environment set-up on Windows.

  • asd

    dasdasd