Skip to main content

Backbone.js - Creating a RESTful CRUD application

I've been trying to build a CRUD application using Backbone.js and was able to retrieve a set of records into a collection using the fetch method. To perform other operations i.e. Create, Update and Delete I could always invoke Backbone.sync but I was exploring on the lines where this is performed implicitly. The fetch method sends an implicit GET request on url specified in the Collection, similarly I was looking for other methods that allow you to send POST, PUT and DELETE requests to the url. While I was building this application, I did come across a condition where Backbone was not sending a request. I was finally able to figure out as to why that happened and then it was a simple fix in my Backbone application as well as in the REST service.

Create - Add a New record to the Collection:

Adding a new record to the Collection is fairly simple. Call create method on the Collection and you would see that a POST request is sent on the URL specified in the Collection. The POST request sends the Model data in the request body in JSON format (Content-Type as application/json):
carCollectionInstance.create({BRAND: 'Hyundai', MODEL: 'i20', COLOR: 'GREY'});

Retrieve - Retrieving a set of records and adding it to the Collection:

Retrieving a set of records by sending a GET request to the server and then parsing the response using parse property is also very simple. I have explained this in my last blog post - 'Backbone.js - Parsing the response from the Server'

Delete - Deleting the models in the Collection:

This is where I had spent most of my time in figuring out as to why Backbone wouldn't send a DELETE request to the server. There were two issues here; firstly the records that the REST service returned in JSON format didn't contain the 'id' key:

{BRAND: "Ford", MODEL: "Figo", COLOR: "RED"}
{BRAND: "Honda", MODEL: "CRV", COLOR: "GREEN"}
....

I was trying to retrieve a Model from the Collection by calling collection.at(index) and then calling destroy on the Model. The destroy method indeed removed the Model from the Collection but didn't send a DELETE request to my REST service. I was frustrated to see this behavior, then I referred to the docs and understood that if a call to Model.isNew() evaluates to true then the DELETE request wouldn't be sent. Also, according to the docs a Model is considered to be new (isNew() returns true) if the Model doesn't contain an 'id' attribute. This meant that I had to change my REST service and ensure that each Model had an 'id'.

After updating the server side logic, I tried calling destroy on the retrieved Model. Even this time it didn't send a DELETE request. Soon I realized that when ColdFusion serializes any type to JSON, it changes the casing of its keys to uppercase. This is not an issue with Backbone, but in fact a bug in ColdFusion. I had to change the casing of the keys in the parse function before adding the retrieved models to Collection. The next attempt destroy the Model in the Collection and sending a DELETE request to the server worked fine. Backbone would send a DELETE request to the server by specifying the Model's 'id' as a PathParam in the url:

http://localhost:8500/rest/Car/CarService/5

The REST resource would then retrieve the 'id' value which is available as an argument.

Update - Updating the models in the Collection:

The Update mechanism is very much similar to Delete. Here a PUT request is sent to the server specifying the 'id' value in the url and also the Model data in the request body.

resultModels = carCollectionInstance.where({brand: 'Honda'});
for(var i=0; i< resultModels.length; i++) { resultModels[i].set('color','BLACK'); resultModels[i].save(); }


In this example, I'm trying to retrieve a set a records in the Collection where 'brand' is 'Honda'. This would return an array of Models which can then be updated by setting the necessary keys (model.set('key','value')) in the Model. On calling save on the Model, a PUT request would be sent to the server.

This was a good exercise for me and Backbone does a good job by implicitly calling Backbone.sync method  with appropriate HTTP methods. So far I'm very impressed with what Backbone has been able to provide. I've not explored Router and Views in Backbone yet and I'm sure there will be tidbits that I'll discover while learning them. Stay tuned.

Comments

  1. Hi,

    I have the same problem with de DELETE method.
    In the following link:
    https://github.com/documentcloud/backbone/issues/37


    References the attribute "idAttribute".
    With "idAttribute" you do not need to define an id in the Model.

    Example:
    var Car = Backbone.Model.extend({
    idAttribute: "chasis_num",
    initialize: function(){
    ...
    ...
    }
    })

    var car1 = new Car({ chasis_num : '1234567' })
    ..
    //add car1 to collection
    ..
    car1.destroy()

    *) In destroy of car1 will send a DELETE:
    http://localhost:8500/rest/Car/CarService/1234567

    Reference
    http://backbonejs.org/#Model-idAttribute
    I dont know if "idAttribute" fits your needs.

    Thanks for the tutorials of Backbone.js!!
    Martin.

    ReplyDelete
  2. Hi,

    I have the same problem with de DELETE method.
    In the following link:
    https://github.com/documentcloud/backbone/issues/37

    References the attribute "idAttribute".
    With "idAttribute" you do not need to define an id in the Model.

    Example:

    var Car = Backbone.Model.extend({
    idAttribute: "chasis_num",
    initialize: function(){
    ...
    ...
    }
    })

    var car1 = new Car({ chasis_num : '1234567' })
    ..
    //add car1 to collection
    ..
    car1.destroy()

    *) In destroy of car1 will send a DELETE:
    http://localhost:8500/rest/Car/CarService/1234567

    Reference
    http://backbonejs.org/#Model-idAttribute
    I dont know if "idAttribute" fits your needs.

    Thanks for the tutorials of Backbone.js!!
    Martin.

    ReplyDelete
  3. Thanks Martin, for pointing that out. Yes, I read the documentation, perhaps I should have mentioned that in my post. In this scenario I was using a Car collection where none of the attributes would qualify as an identifier for that Model and hence I had to use the 'id' attribute.

    ReplyDelete

Post a Comment

Popular posts from this blog

File upload and Progress events with HTML5 XmlHttpRequest Level 2

The XmlHttpRequest Level 2 specification adds several enhancements to the XmlHttpRequest object. Last week I had blogged about cross-origin-requests and how it is different from Flash\Silverlight's approach .  With Level 2 specification one can upload the file to the server by passing the file object to the send method. In this post I'll try to explore uploading file using XmlHttpRequest 2 in conjunction with the progress events. I'll also provide a description on the new HTML5 tag -  progress which can be updated while the file is being uploaded to the server. And of course, some ColdFusion code that will show how the file is accepted and stored on the server directory.

Server sent events with HTML5 and ColdFusion

There are several ways to interact with the server apart from the traditional request\response and refresh all protocol. They are polling, long polling, Ajax and Websockets ( pusherapp ). Of all these Ajax and Websockets have been very popular. There is another way to interact with the server such that the server can send notifications to the client using Server Sent Events (SSE) . SSE is a part of HTML5 spec:  http://dev.w3.org/html5/eventsource/

Adding beforeRender and afterRender functions to a Backbone View

I was working on a Backbone application that updated the DOM when a response was received from the server. In a Backbone View, the initialize method would perform some operations and then call the render method to update the view. This worked fine, however there was scenario where in I wanted to perform some tasks before and after rendering the view. This can be considered as firing an event before and after the function had completed its execution. I found a very simple way to do this with Underscore's wrap method.