Ember Data - It's Not Just for JSON API Anymore

Ember Data - It's Not Just for JSON API Anymore

Have you ever been completely stumped by a nonstandard API and integrating it with Ember Data? Ember Data “just works” with JSON API out of the box, so often you do not need to know much about the magic behind the scenes. When you have a nonstandard API, however, things can get hairy quick.

We’ll cover how to leverage the Ember Data hooks to bridge the gap, doing things like manually adding in links and relationships, for side loading data, combining multiple API calls together into one response, changing the entire structure of your data, adding and removing default records and much, much more!

Cdfdbfe6892d285dda2b277d14fd7303?s=128

Robert Wagner

March 18, 2019
Tweet

Transcript

  1. 2.

    !2 Robbie Wagner (@rwwagner90) Who am I? The Captain @

    Ship Shape ember-math-helpers, ember-shepherd, Shepherd.js, Ember Inspector
  2. 3.

    !3 Ember Data is a library for robustly managing data

    in applications built with Ember.js. It is designed to be agnostic to the underlying persistence mechanism, so it works just as well with JSON API over HTTP as it does with streaming WebSockets or local IndexedDB storage. While this is technically true, your life is a lot easier if you follow JSON API or REST standards, as there is built in support for them. What is Ember Data? Ember Data ~
  3. 4.

    !4 What is JSON API? It incorporates things like links

    and relationships for pagination, side loading included data, and various other things. Unsurprisingly, since Yehuda Katz is both heavily involved in JSON API, and Ember, it has become the de facto standard for Ember Data. “A specification for building APIs in JSON.”
  4. 6.

    !6 This is all we need right? YES!, IF YOU

    USE JSON API Ember Data converts everything to JSON API, behind the scenes, so if you use JSON API correctly, you shouldn’t need to do any custom adapters or serializers in your apps.
  5. 7.

    !7 What if your API is non-standard? Customizing Ember Data

    While working with Netflix on a project, we had a need to accommodate some new APIs, in Ember Data, that did not conform to JSON API or REST standards. I was immediately aware it would take a lot of customization to support these use cases. I spent several months doing Ember Data customizations and got countless hours of help from both runspired and David Tang. Their help, and David’s blog posts really helped me learn the true depth of customizations possible with Ember Data. I implemented these workarounds and wrote my own blog posts on some of the use cases, and wanted to share what I learned in a more formal talk.
  6. 8.

    !8 Adapters vs Serializers What’s the difference? Which should I

    use when? 01 03 Custom Normalization and Serialization How do we manually force links and relationships on our data, so we can trick Ember Data into thinking we are using JSON API? Handling Errors Gracefully How can we do find or create functionality when the API sends back a 404? 02 04 Common Hooks and Methods When manually manipulating data, which hooks should we use and in which files? When are all these hooks called and when are they not?
  7. 10.

    !10 Adapters Adapters help Ember Data connect with your API,

    but do not typically change the data format. Serializers Serializers take the requests and responses and massage them into a usable format.
  8. 11.

    !11 ๏ Used to customize things like host, namespace, path,

    etc. Anything you need to change to ensure you are hitting the correct endpoint for the model. ๏ Have async capabilities, so they are a good place to make multiple requests and combine them into one response. Adapters
  9. 13.

    !13 ๏ Used to massage data responses sent to you

    from the API, and the requests you send back to the API. ๏ Have several hooks available, which run at specific times, to allow for only transforming the data in the exact situations you want. ๏ Can use EmbeddedRecordsMixin to indicate data of other model types is embedded in the response, rather than requiring a separate API call Serializers
  10. 16.

    !16 Creating a Default Record When a belongsTo Request Errors

    200 vs 404 With JSON API, a collection without any records would return a 200 with an empty response, so your app would still be able to function. In a non-standard environment, your API will potentially 404, causing the app to enter an error state, and the app will no longer be usable. To get around the error thrown by the back end, we’ll want to manually catch it and return a default record instead. We can handle this in the findBelongsTo hook of the parent adapter, to ensure when it tries to load the belongsTo relationship, it gives back a default record, instead of putting the app in an error state. We will use a person -> address relationship as an example of how to accomplish this.
  11. 17.

    !17 Person Model Our person model only has a relationship

    set up to the address model. It is an async. relationship, so the call to get the address will happen automatically Address Model Our address model contains all the typical address fields you would expect, for any typical address.
  12. 20.

    !20 Ad Hoc Relationships with Ember Data Website -> page

    -> pageViewStats Imagine you have a relationship of website -> page - > pageViewStats. The data for all pages’ pageViewStats is returned as one big response for the entire website from an endpoint of the form /api/v3/ websites/{websiteId}/pageViewStats The pageViewStats data can be fetched all at once, and stored on the website model, but we wanted to setup some manual relationships and be able to access the pageViewStats data on each page model under page.pageViewStats. This requires some extra setup, to get everything working.
  13. 21.

    !21 Website Model First we need to define our website

    model, with a hasMany relationship for pageViewStats Website Serializer Since we did not have a url that conformed to REST standards, we had to do some magic to add this relationship into links
  14. 22.

    !22 Manually triggering hasMany hasMany doesn’t get loaded automatically? Since

    we are not using the pageViewStats relationship on the website model directly, it does not know to load all the records. To force things to load, we can manually call .load() on the hasMany relationship
  15. 23.

    !23 Page Model We define a pageViewStats relationship on the

    page we want to access them from pageViewStat Model We also define the inverse relationship to the page the pageViewStats belong to
  16. 24.

    !24 Manually Setting the Relationship THE BEST OF BOTH WORLDS!

    This allowed us to have one aggregated data set sent down from one API call, which populated the hasMany, and also allowed us to access the specific pageViewStat records associated with each page record by just checking page.pageViewStats, which accomplished our ultimate goal of displaying a table of page records, and listing the pageViewStat values for each.
  17. 26.

    !26 ๏ createRecord, deleteRecord, and updateRecord are all called to

    do things you might suspect, create, update, and delete records. ๏ findAll, findRecord, etc. are the methods you will call to query for the records, so if you need to tweak that functionality, this is where to do it. Adapter Methods
  18. 27.

    !27 Using POST For Updates Updating records defaults to PUT/PATCH

    By manually invoking a POST, via ember-ajax, we can ensure both creates and updates use POST, if our API does not support the default PUT/PATCH behavior.
  19. 28.

    !28 ๏ normalizeResponse is used to normalize a payload from

    the server to a JSON API Document. Only executed when you do an explicit API call for that resource type, so if you are trying to massage data for a manually added relationship, you have to do so in the parent. ๏ serialize is used when a record is saved in order to convert the record into the form that your external data source expects. ๏ Normalization is converting the data from the server into a format for your API to consume, and serialization is converting the data back into the format your server expects. ๏ Serialize -> Server Base Serializer Metods
  20. 29.

    !29 ๏ normalizeFindAllResponse and normalizeFindRecordResponse can be used to do

    different normalization dependent on whether you are finding one record or many. ๏ serializeBelongsTo and serializeHasMany etc. allow you to serialize relationships differently based on whether it is a single record or many as well. ๏ extractRelationship can be used to change the relationship type, if the relationship name from the back end is different than your model name on the front end. JSONSerializer/ RESTSerializer Methods
  21. 30.

    !30 Normalizing Records normalizeFindAllResponse VS normalizeFindRecordResponse We can use these

    methods to set things like links on each record, and set them to non-standard URLs, but for findAll loop through the array or for findRecord just set it directly on the single record.
  22. 31.

    !31 Differing Relationship Names for Client and Server CHANGING THE

    RELATIONSHIP TYPE In this example we have a polymorphic foo relationship, which can be of types bar or baz on the front end. However, the back end denotes these types as A and B, so we need to switch these type names both incoming and outgoing.
  23. 32.

    !32 Serializer Methods TONS OF CUSTOMIZATION OPTIONS There are quite

    a few methods on the provided serializers, like the JSONSerializer, which will allow you to handle almost any scenario where the data might need to be massaged.