AMS with jsonapi: underscored vs dasherized types


#1

ActiveModelSerializer with jsonapi underscores the “type” attribute, whereas ember-data expects dasherized object types.

Eg: "type": "contact_address" vs "type": "contact-address".

Is there a way to customize DS.JSONAPISerializer to support underscored object types?


#2

In my Rails 5 & Ember Data tutorial I used a model called PublishingHouse. Also using AMS with JSON API. What I did was modify the Rails routes:

resources :publishing_houses, :path => '/publishing-houses'

Although you could attain the same effect by overriding some normalize* method in your JSONAPISerializer, converting the type casing. Not sure whether the keyFor* methods could be of any help in this case (if you don’t want to go down the normalize* route). If that helps somewhat, there’s more here.


#3

I ran into the same problem last night and had to modify the application’s DS.JSONAPISerializer. Here is my entire app/serializers/application.js file:

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({
  keyForAttribute: function(attr, method) {
    return Ember.String.underscore(attr);
  }
});

This worked for me.


#4

Curious, doesn’t this apply to attributes only, I mean, inside the attributes object? Or also to id and type?


#5

My previous post was about attributes, good observation @emberigniter. I was unclear about exactly what “support” meant by OP, e.g. support could mean 1) hitting the rails endpoint or 2) parsing the response JSON. Both caused problems for me with the default AMS JSON API adapter.

My previous post addressed issue 2. IIUC, Serializers translate from backends to Ember Data, and handle inbound concerns. The inbound concern of the AMS JSON API response is mainly to take attributes in the response which are snake_cased and map them to the conventional camelCased attributes in the corresponding DS.Model, e.g. this response:

{
    "data" : {
        "type" :  "resource_types",
        "attributes" : {
            "attr_one" : "value1",
            "attr_two" : "value2"
        }
        ...
    }
}

should map to this model:

import DS from 'ember-data';

export default DS.Model.extend({
    attrOne: DS.attr('string'),
    attrTwo: DS.attr('string')
});

I performed that mapping in app/serializers/application.js, shown in my previous post.

To address issue 1 though, I also had to modify the application Adapter as well. Adapters translate from Ember Data to backends, and thus address outbound concerns. To be able to hit the correct endpoint on the rails server, the Ember Data model name has to be converted to camel_case and also pluralized. Ember Data would send a request to /resourceType instead of /resource_types as Rails usually expects). I did this in app/adapters/application.js:

import DS from 'ember-data';

export default DS.JSONAPIAdapter.extend({
    pathForType: function(modelName) {
        return Ember.String.pluralize(Ember.String.underscore(modelName));
    },
});

It seems like a strange series of things to do because both Ember Data and ActiveModelSerializers speak JSON API. But, it’s a very small amount of code and it makes sense to have to do. They know about the structure of the JSON API response, but the specification is lax on naming conventions inside of the structure.

tl;dr Handle inbound and outbound translation concerns in Serializers and Adapters.


#6

Hey @amw_zero, great that it works!

I disagree though with your classification of inbound and outbound. Adapters and serializers serve a different purpose.

The adapter manages the location of resources (i.e deals with URLs, paths, headers, XHR) and the serializer translates the content (usually JSON; normalize* methods for inbound content, serialize* methods for outbound content). An analogy I use in the I article cited above is the following:

Analogous to calling someone in the Netherlands, the Adapter would serve to prepend the +31 calling code to our destination. The Serializer itself would be the required Dutch-to-English live interpreter, even when both languages share the same Latin alphabet.


#7

Thanks for the distinction. Inbound/outbound was how it clicked in my head. It seems like a Serializer operates on both incoming and outgoing data, though, so it breaks down there.

The phone analogy is quite good. Thanks!