Accessing Metadata from Ember Data's findRecord Method
Learn how to populate Ember Data Models with data from outside the attributes property of a JSON payload.
Summary
In this video, we'll learn how to access metadata from a JSONAPI response that we get after calling Ember Data's findRecord
method.
This seems straightforward, as the Ember guides show how we can get a meta object off of a query
response. However, the method outlined in the guides only works when using the query
method, and not the findRecord
method[^1].
If we try to get the meta object off the response in the model, we'll see that it is undefined
:
model() {
return this.store.findRecord('user', params.user_id).then(author => {
console.log(author.meta); // => undefined
return author;
});
}
The metadata is disappearing somewhere between the network response and Ember Data's store. This means we need to look at making a custom Serializer.
We can generate a serializer for our User model with
ember g serializer user
Then we can overwrite its normalizeResponse
method in our new file.
The normalizeResponse
recieves several arguments, and we're interested in the payload
argument. This contains the entire, raw JSON response that the API sends to our app. Inside our payload
we can see the meta
property being returned, so we can pull out properties we care about:
const topics = payload.meta.topics;
const favorites = payload.meta.favorites;
Finally, we can shove them into the payload.data.attributes
object, so Ember Data can read them from the place it expects – data
.
payload.data.attributes.topics = topics;
payload.data.attributes.favorites = favorites;
For this to work, we'll also need to create topics
and favorites
properties on user
model, so Ember Data has a place to store this data.
Now, we can see topics
and favorites
are able to read off the user model, even though they originate from the meta
object on the response JSON.
Finally, since these attributes are not "real" user properties, we need to tell the serializer to ignore them when sending a user model outbound – in the case of a PATCH request, for instance. We don't want topics
and favorites
to be sent as properties of a user
to the API.
To do this, we can use the attrs
property in the serializer to specify which properties we want serialized from our model.
attrs = {
topics: { serialize: false },
favorites: { serialize: false },
};
Our final serializer looks like this:
// app/serializers/user.js
import JSONAPISerializer from "@ember-data/serializer/json-api";
export default class UserSerializer extends JSONAPISerializer {
attrs = {
topics: { serialize: false },
favorites: { serialize: false },
};
normalizeResponse(store, primaryModelClass, payload) {
if (payload.meta) {
const topics = payload.meta.topics;
const favorites = payload.meta.favorites;
payload.data.attributes.topics = topics;
payload.data.attributes.favorites = favorites;
}
return super.normalizeResponse(...arguments);
}
}
[^1]: There has been some discussion about this in the Ember community, see this GitHub issue which covers some reasoning behind the lack of first party metadata support and proposes the solution I present here.