To this point we have glossed over both Serializers and Adapters. We briefly
looked at an adapter as we customized the
host
property and looked at changing
headers
and
namespace
properties. We haven't really looked at serializers at
all and we have gotten away with that to this point because where we have used a
backend we have used one that serves up a JSON API compliant payload. But, what
if we had a non-compliant payload. Well, I have set up a basic rails 5 api only
app
here
There is a link to
the repo in the notes. This app serves up a payload that looks like this:
[
{
"id": 4,
"title": "Community",
"description": "Community is an American television sitcom created by Dan Harmon that premiered on NBC on September 17, 2009. The single-camera series follows an ensemble cast of characters played by Joel McHale, Gillian Jacobs, Danny Pudi, Yvette Nicole Brown, Alison Brie, Donald Glover, Ken Jeong, Chevy Chase, and Jim Rash at a community college in the fictional town of Greendale, Colorado. It makes heavy use of meta-humor and pop culture references, often parodying film and television clichés and tropes.",
"created_at": "2016-09-20T00:55:43.437Z",
"updated_at": "2016-09-20T00:55:43.437Z"
},
{
"id": 5,
"title": "Chuck",
"description": "Chuck is an American action-comedy/spy-drama television series created by Josh Schwartz and Chris Fedak. The series is about an average computer-whiz-next-door named Chuck, played by Zachary Levi, who receives an encoded e-mail from an old college friend now working for the Central Intelligence Agency (CIA).",
"created_at": "2016-09-20T00:55:43.443Z",
"updated_at": "2016-09-20T00:55:43.443Z"
},
{
"id": 6,
"title": "Star Trek: The Next Generation",
"description": "Star Trek: The Next Generation (abbreviated as TNG and ST:TNG) is an American science fiction television series created by Gene Roddenberry that ran between 1987 and 1994. Roddenberry, Maurice Hurley, Rick Berman, and Michael Piller served as executive producers at different times throughout its production.",
"created_at": "2016-09-20T00:55:43.446Z",
"updated_at": "2016-09-20T00:55:43.446Z"
},
{
"id": 7,
"title": "The X-Files",
"description": "The X-Files is an American science fiction horror drama television series created by Chris Carter. The program originally aired from September 10, 1993, to May 19, 2002, on Fox, spanning nine seasons, with 202 episodes and a feature film of the same name, before returning with a second film in 2008 and a six-episode tenth season in 2016. The series revolves around FBI special agents Fox Mulder (David Duchovny) and Dana Scully (Gillian Anderson) who investigate X-Files: marginalized, unsolved cases involving paranormal phenomena.",
"created_at": "2016-09-20T00:55:43.448Z",
"updated_at": "2016-09-20T00:55:43.448Z"
}
]
As you can see, this is not a JSON API compliant payload. It is just some JSON - an array of objects. So, the question is how do we get Ember to consume this payload and produce models that we can use. To follow along, make sure your rails server is up and running. First, let's bootstrap an Ember app.
ember new show_frontend
cd show_frontend
ember g model show title:string description:string # perhaps not the best model name nonetheless...
ember g route index
vim app/routes/index.js
app/routes/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return this.store.findAll('show');
}
});
app/templates/index.js:
<ul>
{{#each model as |show|}}
<li><strong>{{show.title}}</strong> - {{show.description}}</li>
{{/each}}
</ul>
Now, let's create an adapter to tell our ember app where to get its data from. Then let's open up our new adapter and add a host.
ember g adapter application
vim app/adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host: 'http://localhost:3000'
});
Now, if we start our Ember server and navigate to
http://localhost:4200
we can
see in our console some helpful errors that basically just say that the JSON
document that we received from our server is not formatted like we expect. The
error suggests that we use the serializer's
normalizeResponse
hook to munge
the server response into a JSONAPI compliant document. But, in our case we don't
have to do that. Let's start by generating a serializer:
ember g serializer application
Then let's open it up:
vim app/serializers/application.js
In our case, all we have to do to make this work is change
export default
DS.JSONAPISerializer.extend
to
export default DS.JSONSerializer.extend
. The
JSONSerializer does not expect JSON API document, but a very basic JSON document
like that being provided by Rails in our example. If your use case is more
complicated, you can use the serializer's
normalizeResponse
hook to make the
document look like we want. Let's change our serializer back to the
JSONAPISerializer and use normalizeResponse to turn our data into a JSONAPI
compliant payload.
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType){
let mungedPayload = {};
mungedPayload.data = payload.map(item => {
let currentItem = { id: item.id, type: primaryModelClass.modelName, attributes: item };
delete currentItem.attributes.id;
return currentItem;
});
return this._super(store, primaryModelClass, mungedPayload, id, requestType);
}
});
So, this is a really naive approach, it obviously works for a findAll request,
but it would be less effective with a findRecord request. Fortunately, Ember
Data gives us access to the request type by way of the
requestType
param. We
could use this param to property format our response depending on the type of
request, or we could use one of the type specific hooks from the serializer.
These functions are called by the serializer depending on the requestType. So,
for example, if we wanted to just override for a findAll request we could use
the
normalizeFindAllResponse
method or we could override the
normalizeArrayResponse
which is called by all of the normalize functions that
return an array like value. So what function you decide to override really
depends on your specific case. Regardless of what function you override, in most
cases it will probably be valuable to call
this._super
and just normalize the
piece of the data that needs work rather than trying to serialize everything
your self, which will probably end up brittle and be hard to upgrade, maintain,
and debug.
That is it for today.
Comments for Serializers - Munging some payloads