Search code examples
javascriptbackbone.jscoffeescript

Prepend absolute URL path in Backbone.sync for an API on another server


My Backbone app communicates with an API that exists on another server. Backbone.sync will generate relative URLs by default. The simplest way to prepend the absolute server path would be something like the following:

MyApp.BASE_URL = "https://api.someOtherSite.com"


class MyApp.Model extends Backbone.Model
  urlRoot: "#{MyApp.BASE_URL}/my_app_url"

However, I'd rather not do this as it isn't DRY. I thought I could try something like the following by overriding Backbone.sync:

do (Backbone) ->
  BASE_URL = 'https://api.someOtherSite.com'
  baseSync = Backbone.sync

  Backbone.sync = (method, model, options) ->
    url = _.result(model, 'url')
    options.url = "#{BASE_URL}/#{url}" if url && !options.url

    baseSync method, model, options

However, this is problematic for other parts of the code as the options object gets passed around all over the place. *(explanation for the interested at the bottom)

Is there a recommended clean and DRY way to prepend a server path to the front of all URLs generated by Backbone.sync?


*If the model being synced is an instance of Backbone.Collection this options object will get passed to the collection's model constructor. URL is one of the few properties that will get directly attached to a model if it is passed in as a part of the options object. This breaks sync for any models that are created in a collection as they now have a set URL attached to them instead of a url method that generates an appropriate url by using urlRoot or the collection's url.


Solution

  • I think the best option is to create a base model that your other models will extend from:

    MyApp.Model = Backbone.Model.extend({
      urlRoot: function(){
                 return 'https://api.someOtherSite.com/' + this.get('urlFragment')
      }
    });
    

    And then have your models be defined like so

    MyApp.MyModel = MyApp.Model.extend({
      urlFragment: "my_model_url"
    })
    

    So, in CoffeeScript:

    class MyApp.Model extends Backbone.Model
      urlRoot: ->
               'https://api.someOtherSite.com/' + @get('urlFragment')
    

    And

    class MyApp.MyModel extends MyApp.Model
      urlFragment: "my_model_url"
    

    Now you've got your urls specified in a DRY fashion!