[SOLVED] EmbeddedRecordsMixin for JSON:API data structures?


I apologize for the repost on this one, but I’ve run out of ideas and my hope was that posting under a more appropriate title at this point might help find the right answers from the right people. I added the following to [SOLVED] Ember Data JSON API included relationships and I was hoping I might be able to spur more discussion.

What I see essentially is that the embedded records are in the Ember data store, but that they are not be rendered in my HBS template.

The data model is defined as:

import DS from ‘ember-data’; import BaseObject from ‘./base-object’;

export default BaseObject.extend({ ‘phones’: DS.hasMany(‘phone’, { async: false }), ‘addresses’: DS.hasMany(‘address’, { async: false }), ‘emails’: DS.hasMany(‘email’, { async: false }), ‘websites’: DS.hasMany(‘website’, { async: false }), });

I have an associated serializer definition using the EmbeddedRecordsMixin for this particular model, where the application serializer is a subclass of JSONAPISerializer:

import Ember from ‘ember’; import DS from ‘ember-data’; import ApplicationSerializer from ‘./application’;

export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, { attrs: { addresses: { embedded: ‘always’ }, emails: { embedded: ‘always’ }, phones: { embedded: ‘always’ }, websites: { embedded: ‘always’ } } });

The server is responding to the request with this data structure:

{ “data”: { “type”: “contact-datum”, “id”: “1”, “attributes”: { “updated-on”: null, “object-state”: “SAVED”, “created-on”: “2016-01-29”, “updated-by”: null, “uuid”: “35ee70fd-51e1-4fb2-97e7-521532bb8638”, “created-by”: “SYSTEM” }, “relationships”: { “websites”: { “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/relationships/websites”, “related”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/websites” } }, “emails”: { “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/relationships/emails”, “related”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/emails” } }, “addresses”: { “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/relationships/addresses”, “related”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/addresses” } }, “phones”: { “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/relationships/phones”, “related”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1/phones” } } }, “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/contact-datum/1” } }, “included”: [ { “type”: “websites”, “id”: “1”, “attributes”: { “updated-on”: null, “object-state”: “SAVED”, “created-on”: “2016-01-29”, “updated-by”: null, “type”: “BUSINESS”, “uuid”: “6c2535eb-14b1-4cc6-90a7-be9807559341”, “created-by”: “SYSTEM”, “url”: “www.xxxx.com” }, “relationships”: {}, “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/websites/1” } }, { “type”: “emails”, “id”: “1”, “attributes”: { “address”: "info@xxxx.com", “updated-on”: null, “object-state”: “SAVED”, “created-on”: “2016-01-29”, “updated-by”: null, “type”: “BUSINESS”, “uuid”: “ad9876a5-03dd-4a74-96f8-fb0796e479a4”, “created-by”: “SYSTEM” }, “relationships”: {}, “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/emails/1” } }, { “type”: “addresses”, “id”: “1”, “attributes”: { “country”: null, “city”: “Cleveland”, “created-on”: “2016-01-29”, “updated-by”: null, “type”: “BUSINESS”, “uuid”: “dd6b7661-7be1-4f9b-b0df-90e466e4f6ca”, “zipcode”: “44101”, “updated-on”: null, “object-state”: “SAVED”, “street1”: “100 Pine Road”, “state”: “OH”, “street2”: “Suite 125”, “created-by”: “SYSTEM” }, “relationships”: {}, “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/addresses/1” } }, { “type”: “phones”, “id”: “1”, “attributes”: { “number”: “216-555-6446”, “updated-on”: null, “object-state”: “SAVED”, “created-on”: “2016-01-29”, “updated-by”: null, “type”: “BUSINESS”, “uuid”: “95a6c3a1-25e3-4153-b25e-4cf6d54bfd61”, “created-by”: “SYSTEM” }, “relationships”: {}, “links”: { “self”: “http://docker:28080/bv-core/domain/jsonapi/v1/phones/1” } } ] }

As I said, according to the Ember browser plugin, the records appear to be there and be properly linked together. However, the values do not show up when the template is rendered. I added some logging to the template and I’m seeing the following information relative to the "addresses" when I attempt to render them:

WARNING: You have pushed a record of type ‘contact-data’ with ‘phones’ as a link, but the association is not an async relationship. ember.debug.js:5533 WARNING: You have pushed a record of type ‘contact-data’ with ‘addresses’ as a link, but the association is not an async relationship. ember.debug.js:5533 WARNING: You have pushed a record of type ‘contact-data’ with ‘emails’ as a link, but the association is not an async relationship. ember.debug.js:5533 WARNING: You have pushed a record of type ‘contact-data’ with ‘websites’ as a link, but the association is not an async relationship. ember.debug.js:7866 Addresses Class {canonicalState: Array[0], store: Class, relationship: ember$data$lib$system$relationships$state$has$many$$ManyRelationship, record: InternalModel, currentState: Array[0]…}[]: (…)get []: GETTER_FUNCTION()set []: SETTER_FUNCTION(value)ember1454094611514: null__ember_meta: Object__nextSuper: undefinedcanonicalState: Array[0]currentState: Array[0]isLoaded: trueisPolymorphic: undefinedrecord: InternalModelrelationship: ember$data$lib$system$relationships$state$has$many$$ManyRelationshipstore: Classtype: bv-ember-application@model:address:proto: Class@each: ComputedProperty[]: Object__ember1454094611514: null__ember_meta__: Object_super: superFunction()addArrayObserver: (target, opts)addEnumerableObserver: (target, opts)addObject: (obj)addObjects: (objects)addRecord: (record)any: (callback, target)anyBy: ()arrayContentDidChange: (startIdx, removeAmt, addAmt)arrayContentWillChange: (startIdx, removeAmt, addAmt)canonicalState: nullclear: ()compact: ()constructor: DS.ManyArraycontains: (obj)createRecord: (hash)currentState: nullenumerableContentDidChange: (removing, adding)enumerableContentWillChange: (removing, adding)every: (callback, target)everyBy: ()everyProperty: ()filter: (callback, target)filterBy: (key, value)filterProperty: ()find: (callback, target)findBy: (key, value)findProperty: ()firstObject: ObjectflushCanonical: ()forEach: (callback, target)getEach: (key)has: (name)hasArrayObservers: ComputedPropertyhasEnumerableObservers: ComputedPropertyindexOf: (object, startAt)init: ()insertAt: (idx, object)internalAddRecords: (records, idx)internalRemoveRecords: (records)internalReplace: (idx, amt, objects)invoke: (methodName)isAny: (key, value)isEvery: (key, value)isLoaded: falseisPolymorphic: falselastIndexOf: (object, startAt)lastObject: Objectlength: 0loadedRecord: ()loadingRecordsCount: (count)map: (callback, target)mapBy: (key)mapProperty: ()meta: nullnextObject: (idx)objectAt: (index)objectsAt: (indexes)off: (name, target, method)on: (name, target, method)one: (name, target, method)popObject: ()promise: nullpushObject: (obj)pushObjects: (objects)record: nullreduce: (callback, initialValue, reducerProperty)reject: (callback, target)rejectBy: (key, value)rejectProperty: ()relationship: nullreload: ()removeArrayObserver: (target, opts)removeAt: (start, len)removeEnumerableObserver: (target, opts)removeObject: (obj)removeObjects: (objects)removeRecord: (record)replace: (idx, amt, objects)reverseObjects: ()save: ()setEach: (key, value)setObjects: (objects)shiftObject: ()slice: (beginIndex, endIndex)some: ()someProperty: ()sortBy: ()toArray: ()trigger: (name)uniq: ()unshiftObject: (obj)unshiftObjects: (objects)without: (value)proto: Class

The logging that is inside the #each for the addresses array never logs anything and appears to never be entered.

Does anyone have any further thoughts based on these details? Can anyone provide pointers to information on how to debug this situation?

Thanks again, Craig


I recently tried using EmbeddedRecordsMixin with JSON-API and I dont think it works. It does work for APIs based on the RESTSerializer or JSONSerializer conventions though. My guess is that if an API is adhering to JSON-API, then it should follow the spec and not deviate from it, hence why there is a spec.



Can you give any more details? My reading of the spec and, in particular, http://jsonapi.org/format/#fetching-includes makes me believe that this type of “embedded records” functionality is supported by JSON:API. Does that mean that I’m misreading the spec, that EmbeddedRecordsMixin isn’t capable of handling that functionality or something else entirely?

Thanks for your insights! Craig


@Craig_Setera check out the section on JSON-API in a blog post I wrote recently: http://thejsguy.com/2015/12/05/which-ember-data-serializer-should-i-use.html.

Embedded records are different than sideloading using “included”. I think of embedded records as nested JSON data, and as far as I’m concerned, only the RESTSerializer and JSONSerializer support handling that.


I updated [SOLVED] Ember Data JSON API included relationships with updates on this issue… all my fault!