Expected GET responses

I read this article and would like a little clarification and then have a question for handling a situation.

It appears the expected response for a GET or POST is something like this:

{ 
    "post" : {
        id: 1,
        comments: [1,2,3]
    },
    "comments" : [
        {
            id: 1,
            comment: "Bob Loblaw's Law Blog"
        }, {...}, {...}
    ]
}

Or, since Ember-data supports a polymorphic model property, here is another possibility:

{
    "event" : {
        id: 1,
        mainContact: {
            id: 23,
            type: "contact" // makes it polymorphic
        }
    }
}

So that tells Ember-data that “mainContact” is an instance of “contact”. It creates an empty App.Contact and awaits a request for data off of it before fetching it from the server.

So what about this situation? Consider being handed a URL and navigating to a page that is loading the aforementioned event. Since it’s the first time the system will be loading the event, it will ultimately need all the data associated with it, including all the details about the mainContact (so it doesn’t have to make another request to retrieve that data–though it could and that is what Ember-data seems to expect).

So the response might look like this:

{
    "event" : {
        id: 1,
        mainContact: {
            id: 23,
            firstName: "Kevin",
            lastName: "Beez",
            type: "contact" // makes it polymorphic
        }
    }
}

…or maybe, more properly, like this (with the extra data side-loaded):

{
    "event" : {
        id: 1,
        mainContact: {
            id: 23,
            type: "contact" // makes it polymorphic
        }
    },
    "contacts" : [
        {
            id: 23,
            firstName: "Kevin",
            lastName: "Beez"
        }
    ]
}

Can Ember-data handle this? Is this a valid thought process?

Here’s the research I’ve done so far While debugging, trying to figure out how this could be done, I determined a couple places this could be accomplished.

  1. Using the first method (including the embedded information) and within deserializeRecordId, if the object is polymorphic, add an if-check to say, “if record.isEmpty, here’s the data for it”. I would think that declaring a model with “embedded: always” would make this method believe the parameter has all the data available to create a populated record. That might look like this:

    } else if (typeof id === "object") {
        // polymorphic
        var record = store.recordForId(id.type, id.id);
        if (record.isEmpty) { // or if relationship.options.embedded === "always"
            record.setupData(id);
        }
        data[key] = record;
    }
    
  2. Using the second method seems like it should just work, inherently. Ember-data seems to handle side-loaded data pretty effectively. And it may, I might just be doing it incorrectly. Taking another example, I would think this should work, but it does not:

    {
        "event" : {
            id: 1,
            contact: 23
        },
        "contact" : {
            id: 23,
            firstName: "Kevin",
            lastName:  "Beez"
        }
    }
    

Thanks for reading my book!

And just then, the heavens parted; and the knowledge from the Ember gods came in and settled in the poor boy’s little brain.

It’s amazing what happens when you reread “old” documentation once you gain a better understanding of the Ember framework.

So here’s the solution I was looking for. Given the first example, above, with a response like so:

{
    "event" : {
        id: 1,
        mainContact: {
            id: 23,
            firstName: "Kevin",
            lastName: "Beez"
        }
    }
}

Simply extending extractSingle and extractArray in my EventSerializer, I was able to accomplish just what I was looking for:

extractSingle: function(store, type, payload, id, requestType) {
    var mainContact = payload.event.mainContact;
    payload.contacts = [];
    if (mainContact) { // could be null
        payload.contacts.push(mainContact);
        payload.event.mainContact = mainContact.id;
    }
    return this._super.apply(this, arguments);
}

My payload has now been normalized to this:

{
    "event" : {
        id: 1,
        mainContact: 23
    },
    "contacts" : [
        {
            id: 23,
            firstName: "Kevin",
            lastName: "Beez"
        }
    ]
}

And Ember-data eats this up and adds both the contact and the event object to the store and everyone, everywhere, played nicely for the rest of their lives.

Also, notice my misconception about polymorphism in my examples. The need to inject the model type into the payload arose only because the entire object resided where only the id was expected. What I have is mismatched model names (e.g., “mainContact” > “contact”), not necessarily polymorphism. Since my model defines “mainContact” as a “contact”, it handles the conversion automatically. (i.e., mainContact: DS.belongsTo("contact")).

Cool! That was fun!