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.

👋 Hey there, Ember dev!

We hope you enjoyed this free video 🙂

If you like it and want to keep learning with us, we've written a free 6-lesson email course about the fundamental patterns of modern component design in Ember.

To get the first lesson now, enter your best email address below:

You can also check out more details about the course by clicking here.

Questions?

Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.