Monday, 3 February 2014

Customising Backbone’s Sync Module

Backbone syncI’ve started using the Backbone MVC JavaScript framework recently, and have been pleasantly surprised by how easy it is to customise bits of the framework when I need some additional functionality.  by
Backbone communicates with backend web services using the Backbone.sync function; this function examine the model object being synchronised to determine whether the operation is a “create”, “read”, “update” or “delete” and then uses jQuery.ajax to perform a HTTP POST, GET, PUT or DELETE on the backend web service.  The change I want to make is to use PROPFIND and PROPPATCH instead of GET and PUT. I may go into the reasons for this change more in subsequent posts, but for now just trust me (please) that there is a reason why I want to do this.
One of the reasons that I chose Backbone was this section from the “Extending Backbone” section in the documentation:
Many JavaScript libraries are meant to be insular and self-enclosed, where you interact with them by calling their public API, but never peek inside at the guts. Backbone.js is not that kind of library.
Because it serves as a foundation for your application, you're meant to extend and enhance it in the ways you see fit
So here goes!  The Backbone.sync function is function (method, model, options) where method = “create” | “read” | “update” | “delete”.  Looking at the source of the function, the first line of the function does this:

var type = methodMap[method];

and the definition of methodMap is:

var methodMap = {
    'create': 'POST',
    'update': 'PUT',
    'patch':  'PATCH',
    'delete': 'DELETE',
    'read':   'GET'
  };


So to use PROPFIND and PROPPATCH instead of GET and PUT I should be able to simply create a different MyApp.methodMap hash (the original variable is private to the anonymous function that defines the Backbone namespace and functions), create a new MyApp.sync function which is an exact copy of Backbone.sync but referencing my new MyApp.methodMap hash and replace Backbone.sync with MyApp.sync.  Something like this:

MyApp.methodMap = {'create': 'POST', 'update': 'PROPPATCH', 'patch':'PATCH', 'delete': 'DELETE', 'read':'PROPFIND'};
MyApp.sync = function (method, model, options) {
     var type = MyApp.methodMap[method];

     // all the rest of Backbone.sync
};
Backbone.sync = MyApp.sync;


But it doesn’t work.  Monitoring the HTTP traffic using Fiddler confirms that the PROPFIND verb is being sent correctly when I call Model.fetch() but my model isn’t actually being populated with the data returned.  Looking further into the original Backbone.sync function, on about the 42nd line it decides whether to process the returned data based upon the HTTP verb that it’s sending:

// Don't process data on a non-GET request.
if (params.type !== 'GET' && !options.emulateJSON) {
  params.processData = false;
}


I just have to replace the ‘GET’ with a ‘PROPFIND’ and my model objects are populated correctly.
So to recap, all I had to do to make Backbone use PROPFIND and PROPPATCH instead of GET and PUT was to:
  1. Create a new MyApp.sync function and MyApp.methodMap hash that were exact copies of the originals.
  2. Modify the new MyApp.sync function to reference MyApp.methodMap.
  3. Modify MyApp.methodMap to return PROPFIND for read and PROPPATCH for update.
  4. Modify MyApp.sync to process returned data for PROPFIND instead of GET.
You may want to make quite different changes to Backbone.sync, but I hope you are inspired by reading this to have a go at it because it isn’t difficult.