Nested Resources with Backbone

The topics I wish to discuss are Backbone and Rails Nested Routes and How do I define nested resources for backbone.js?.

One other thing I wanted to avoid was introducing another plugin in our codebase such as Backbone Relational.

For the sake of this article I will use Articles, Comments and Comment Replies as examples.

Consider the following url structure: /articles/:article_id/comments/:comment_id/comment_replies

This means one article can have several comments, and one article comment can have a several replies.

Consider the following json structure for a comment:

{
  id: 1,
  body: 'Sunny day in London!',
  url_root: '/articles/1000/comments/1'
}

The url_root attribute is very important, since it’s the attribute that will make our lives easier in the moment we need to reach the comment_replies url.

After you prepare your json response and assuming you have a collection, it all starts with setting a url in the collection, like the following:

<h1>Article Page</h1>

<script type="text/javascript">
  // The url param is the one responsible to make
  // all the rest work, this is where everything begins
  var comments = new App.Collections.Comments({ url: '#{ article_comments_path(@article) }'});
</script>

In your Comments Collection make sure you set the url you passed to the collection, this is accomplished in the initialize of the collection, by passing the url as an option.

You will need to have that endpoint if you are thinking about CRUD since Backbone uses that path to make a POST for example.

App.Collections.Comments = Backbone.Collection.extend({
  model: App.Models.Comment,

  url: function() {
    // /articles/:article_id/comments
    return this.baseUrl;
  },

  initialize: function(models, options) {
    this.baseUrl = options.url;
  }
});

The Comment Model is the middle resource between the Article and the Comment Reply, it’s important that one of the attributes is the url with the article_id and the comment id.

App.Models.Comment = Backbone.Model.extend({
  url: function() {
    // /articles/:article_id/comments/1
    return this.get('url_root');
  },

  initialize: function(options) {
    // Here is where we manage to build the nested url
    new App.Models.CommentReply({
      commentPath: this.url()
    });
  }
});

Finally the CommentReply Model receives the url with the necessary attributes in the initialize and sets it in the url function, allowing it to interact with the server.

App.Models.CommentReply = Backbone.Model.extend({
  url: function() {
    // /articles/:article_id/comments/:id/comment_replies
    return this.urlRoot;
  },

  initialize: function(options) {
    // Here we set this model url,
    // which is given by the Comment model through options,
    // which will have the dynamic path we need to reply
    // to the right comment in a given article.
    // /articles/:article_id/comments/1/comment_replies
    this.urlRoot = options.commentPath + '/comment_replies';
  }
});

I hope this gives you the answer you are looking for and I wish you happy coding.

photo of David Silva

Associate Software Engineer
comments powered by Disqus