At Linn we have adopted the pattern of using Hypermedia Controls to represent relationships between resources, for example:

{
	"name": {
		"first" : "Joe",
		"last": "Bloggs"
    },
    "links" : [
    	{ "rel": "country", "href": "/countries/GB" }
    ]
}

But how do you consume an API like this within a Backbone.js app?

For a simple case, where you have a resource with a single link to another, you could override parse and fetch on your Backbone model, something like this:

parse: function (response) {
	var countryLinks  = $.grep(response.links, function(l) {
    	return l.rel == 'country';
    });
    
    if (countryLinks.length) {
		response.country = new Country({
        	url: countryLinks[0].href;
        });
    }
},

fetch: function (options) {
	var self = this;

	return Backbone.Model.prototype.fetch.call(this, options)
    .then(function () {
		return self.country.fetch();
	});
}

Which isn’t too bad. But what if you have multiple links?

parse: function (response) {
	var countryLinks  = $.grep(response.links, function(l) {
    	return l.rel == 'country';
    });
    
    if (countryLinks.length) {
		response.country = new Country({
        	url: countryLinks[0].href;
        });
    }
    
    var timezoneLinks  = $.grep(response.links, function(l) {
    	return l.rel == 'timezone';
    });
    
    if (timezoneLinks.length) {
		response.timezone = new TimeZone({
        	url: timezoneLinks[0].href;
        });
    }
},

fetch: function (options) {
	var self = this;

	return Backbone.Model.prototype.fetch.call(this, options)
    .then(function () {
		return $.when(
        	self.country.fetch(),
            self.timezone.fetch());
	});
}

As the number of links between resources grew this quickly became unwieldy and we knew we needed a more general solution. We knocked our heads together, and came up with a set of extensions for Backbone which would take care of this for us. We have packaged the extensions together into a Backbone plugin called backbone.hypermedia.

Basic Usage

Firstly, your Backbone model must extend Backbone.HypermediaModel instead of Backbone.Model:

var User = Backbone.HypermediaModel.extend({
   ...
});

Then you define a links property for the model like this:

links: {
	'country': Country,
	'timezone': TimeZone
}

Each key corresponds to the rel value of a link which may be present when the model is fetched from the server (we assume a links property on the resource itself like the example earlier). The value defines the type of Backbone model you would like to be constructed based on that relation, so in this example Country and TimeZone are Backbone models (which must themselves extend Backbone.HypermediaModel).

Backbone.HypermediaModel overrides fetch so that for each link found in the response, if there is a corresponding value in the links property on your model, then the related model will also be fetched and a property added to your model under the same key. The return value of fetch is a promise which represents the collective fetch operations for the model and all related resources.

By using the promise returned from fetch you can wait until all related resources are fetched before rendering your view:

user.fetch().then(function () {
	// show user view
});

You can also add a links property to each related resource and have those relations followed as well, if you need to.

We also override toJSON to include any related models which have been added to your model as properties.

And that’s all there is to it. The source is available on GitHub and the package is available through npm, NuGet (thanks to Henry Wilson) and Bower.