Making Mongoid Play Nice With Backbone.js
Backbone has some great features that make it dirt-simple to integrate with a Rails back end. For example, the Backbone models have a .fetch(), .save() and .destroy() method on them. These methods make a call back to your server, based on a url configured on the model. On the server side, you only need to implement the standard CRUD methods of the controller that you routed your Backbone model to, and you have a simple API to CRUD data from the user’s browser, to your back end, and back.
But there’s a problem with Mongoid (the document mapper I’m using for MongoDB) that prevents this from working, out of the box.
The Problem: id vs _id
Mongoid, in your rails code, uses a .id attribute to store a document’s identity. However, there’s a small problem with the way mongoid serializes the document. If you examine the database collection for your document, you’ll see that every mongoid document is stored with an _id instead of an id field. I’m not sure why mongoid does this, but it does. Since mongodb stores documents as BSON (binary JSON) documents, mongoid naturally has a built in mechanism to convert models to json documents. This mechanism is the same whether you are saving to the database or calling .to_json on the model to return it to the browser. The net result is that mongoid sends JSON documents with a _id in them, which Backbone does not recognize.
Because mongoid sends an _id field instead of an id field, Backbone will never try to http “put” your models back to the server, for an update. It will always http “post” them to create a new one.
The Solution: Override Mongoid’s .as_json
The solution to making mongoid play well with Backbone, is to provide an id field in the json that mongoid sends to the browser. To do that, we can override the as_json method of the Mongoid::Document module. This method gets called to generate a json document from a rails model. I’m not actually sure if this is a method that gets before during the execution of to_json, or if to_json is just an alias. Either way, this is the method that you want to override, and here’s how to do it:
In this code, calling the super method of to_json, to get the json representation of the document from mongoid, directly. Then we’re adding an id key with the same value as _id. This allows the json serialization to have an id field the way Backbone wants. Include this module in your rails app and every mongoid document that is serialized to json will include and id field (It will not save the model to the mongodb collection with an “id”, though). This lets mongoid play nice with backbone, and you can now call .save() on your Backbone models knowing that it will create or update your model correctly.
(Note that we’re not removing the _id field in this code, so we do end up with the id duplicated. I’m not sure if removing _id would cause any issues, and we didn’t want to go a lot of work to find out.)