Why Backbone-Relational Sucks?

Apologies for being blunt, but this is what I truly feel after weeks of struggle with Backbone-Relational. Some of the features library boasts are great, but some other problems make its usage horrible. So if you are lured by the cool features this framework claims, here is my candid advice: keep some Disprin tablets along because it is going to give you some serious headaches.

Below are some of the problems I faced using this framework. Here is an dummy data model that I’ll be using throughout my examples. You can skip it for the moment, and refer back to it when you see it being used somewhere.

var Person = Backbone.RelationalModel.extend({
    }),
    PersonCollection = Backbone.Collection.extend({
	model: Person
    }),
    City = Backbone.RelationalModel.extend({
        relations: [{
            type: Backbone.HasMany,
            key: 'residents',
            relatedModel: Person,
            includeInJSON: Backbone.Model.prototype.idAttribute,
            collectionType: PersonCollection,	
            reverseRelation: {
                key: 'city'
            }
        }]
    }),
    CityCollection = Backbone.Collection.extend({
        model: City
    }),
    Company = Backbone.RelationalModel.extend({
        relations: [{
            type: Backbone.HasMany,
            key: 'employees',
            relatedModel: Person,
            includeInJSON: Backbone.Model.prototype.idAttribute,
            collectionType: PersonCollection,	
            reverseRelation: {
                key: 'employer'
            }
        }]
    }),
    CompanyCollection = Backbone.Collection.extend({
	model : Company
    });

Example#1

At the core Backbone-Relational, there is a method Model#fetchRelated(). This is one big fat mouse that will first appear invisible, and when you will eventually find it after hours of computational cycles of your brain, it will drive you nuts (Oops! I am again being subjective?). Here are a few use cases to explain the situation:

// let us initialize a PersonCollection with some specific ids
var models = _.map([3, 6, 9, 13], function( id ){
        return new Person({id:id});
    }),
    persons = new PersonCollection( models );


// fetch the specific ids
persons.fetch()
   .then(function(){
        // person collection is ready here
        // let's print each person with their company name
        persons.each(function( person ){
        /* 
         * person.get('employer') will return only an id,
         * in order to get a full-fledge company object, we need
         * to call fetchRelated(). But guess what, fetchRelated()
         * doesn't return the objects themselves, it returns an
         * array of promises.
         * 
         */
        $.when.apply(null, person.fetchRelated('employer'))
            .then(function(){
                // let's print our data now
                console.log('%s works in %s', person.get('name'), person.get('employer').get('name'));
            });
        });
    });

Before running this snippet, make sure that at least two of the persons mentioned in the list work in the same company. Now prepare yourself for first surprise: If two or more persons share an employer, you will see a log printed for first person alone and an error for the subsequent persons which will say something like this “Cannot call get on undefined”.

Problem Behind fetchRelated()

Google this problem and you’ll practically find nothing. If you debug the issue, you’ll find that fetchRelated() doesn’t return a promise for an entity if a promise had been already returned for it. This behavior, though looks acceptable from logical point of view, makes consumption of the API difficult, because there is no consistent interface to get a related model. Values returned by Model#get() and Model#fetchRelated() vary depending upon internal state of the framework, leaving all the checks and balances to the caller, thus entangling the application developer into infrastructural issues and blurring her focus on business logic.

Workaround

I know it sounds silly, but for the sake for argument, let’s consider this option: fetch all of the companies that you are going to use, as the page loads, so you will never need to invoke person.fetchRelated(‘employer’) and can directly get employer entity using person.get(‘employer’). But this approach becomes almost impossible because:

  1. It cripples the idea of lazy loading reducing initial page load time.
  2. As the application complexity grows, it becomes impossible to perdict which company records you are going to need in the application.
  3. Did I mention it sounds silly?

Example#2

As per the example mentioned on the Backbone-Relational website, consider following situation:

var mozilla = Company.findOrCreate(1);

mozilla.on('add:employees', function( person ){
    person.on('change:city', function( city ){
        console.log('%s lives in %s', person.get('name'), city.get('name'));
    });
    person.fetchRelated('city');
});

mozilla.fetchRelated('employees');

Try running this example, you’ll never see a log.

Workaround

I didn’t have any more juices left in my brain to debug the problem behind, but it worked when I tweaked it a bit like this:

var mozilla = Company.findOrCreate(1);

mozilla.on('add:employees', function( person ){
    person.on('change:city', function( city ){
        console.log('%s lives in %s', person.get('name'), city.get('name'));
    });
});

mozilla.fetchRelated('employees');

setTimeout(function(){
    person.fetchRelated('city');
}, 5000);

Of course it is an ugly workaround, but is there a sane way for the purpose?

Conclusion

I have many more examples in my source code to go on and on, but for a start, let’s be content with two. The crux of the examples above is that Backbone-Relational lacks a uniform interface/contract for accessing relational entities. It is quite possible that I am missing some point that is a per-requisite for properly using the tool, but my humble Googling abilities couldn’t enlighten me with any. If you know the proper way to solve the problems above, please share. They would definitely help me and a lot of other folks. Otherwise I guess this is an opportunity for creation of a new relational framework.

Published by

Umar Ashfaq

Umar Ashfaq is a full-stack web developer. His core strength is building neat UIs with JavaScript on web but he also enjoys server side Java, NodeJS and Objective C. Follow him on twitter @umarashfaq87

7 thoughts on “Why Backbone-Relational Sucks?”

  1. good post Umar, I had about the same experience as the one you described here with Backbone-relational.

    I found it very frustrating to learn and eventually after figuring it out, I realized it wasn’t even capable of what I needed.

    At least it has documentation now though!

  2. Umar, thanks for this post, so what do you recommend about related models? It is not covered anyhow in your next post “The BackboneJS Way of Thinking”.

  3. I experienced the same! Especially implementing it using my API gave me massive headaches. Usually I pick up new technologies quite fast, but this was different. The documentation is lacking on practical examples. I don’t have an alternative though.

      1. Yes, I agree, Angular is much better and faster at making trivial apps than Backbone is. Seriously, this is a todo app dude lol

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>