Ember data's extractArray and extractSingle

Recently I've been playing around with using Node as my api for Ember as opposed to Rails. Ember data plays extremely nice with Active Model Serializers so I've never really had to worry about cleaning up the server's response. Say you have a poll and a poll has many choices, the JSON generated by Active Model Serializers might look something like:

{
  "poll": {
    "id": 1,
    "question": "How long till we get to the moon?",
    "comments": [1, 2, 3]
  },

  "choices": [{
    "id": 1,
    "text": "1 week"
  },
  {
    "id": 2,
    "text": "1 month"
  },
  {
    "id": 3,
    "text": "1 year"
  }]
}

Ember Data's RESTSerializer see's this response and knows exactly what to do with it and the world is safe.
The equivalent response that I was getting with Node looked a little something like this:

{
  "question": "How long till we get to the moon?",
  "_id": "52f7f1ea3aa77f00004c6951",
  "choices": [
    {
      "text": "1 week",
      "_id": "52f7f1ea3aa77f00004c6954"
    },
    {
      "text": "1 month",
      "_id": "52f7f1ea3aa77f00004c6953"
    },
    {
      "text": "1 year",
      "_id": "52f7f1ea3aa77f00004c6952"
    }
  ]
}

The RESTSerializer looks at this response, gets confused and says I'm not going to touch this data. Ember Data is expecting that for a hasMany relationship, that the server is going to respond with an array of IDs i.e. poll: {"choices": [1,2,3]}, and that if the data for the choices is being sideloaded alongside the poll, that they should appear as an array of hashes, with the root being the name of the hasMany i.e. choices.
Before we get to cleaning up this response, we first have to deal with the fact that the primary key is _id as opposed to id. Thankfully there is a simple fix for this.

App.ApplicationSerializer = DS.RESTSerializer.extend({
	primaryKey: '_id'
});

Now lets clean up the response to make it look more like what Ember is expecting. The RESTSerialzer comes with 2 methods that will help us. extractArray and extractSingle. extractArray is called when the server returns a payload representing multiple records, extractSingle is called when the server returns a single record.
For the purpose of this we'll be working with extractSingle.
To use extractSingle we first need to overwrite the poll serializer and call the method:

App.PollSerializer = DS.RESTSerializer.extend({
  extractSingle: function(store, type, payload, id, requestType){
  // Implementation here
  return this._super(store, type, payload, id, requestType)
  }
});
})

Inside of extractSingle, lets grab the embedded choices, and set the polls choices to be an empty array.

var choices = payload.choices;
payload.choices = [];

Now we need to populate payload.choices with the id's of the choices.

choices.forEach(function(choice){
  payload.choices.push(choice._id);
});

Finally we need to update payload

payload = {poll: payload, choices: choices};

And we're done. The final implimentation looks something like:

App.PollSerializer = DS.RESTSerializer.extend({
    extractSingle: function(store, type, payload, id, requestType){
    var choices = payload.choices;
    payload.choices = [];
    choices.forEach(function(choice){
      payload.choices.push(choice._id);
    });
    payload = {poll: payload, choices: choices};
    return this._super(store, type, payload, id, requestType)
    }
  });
})

Now Ember data can do it's thing and the world can breathe easy once again.