Class DS.ActiveModelAdapter

The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate with a JSON API that uses an underscored naming convention instead of camelCasing. It has been designed to work out of the box with the active_model_serializers Ruby gem. This Adapter expects specific settings using ActiveModel::Serializers, embed :ids, embed_in_root: true which sideloads the records.

This adapter extends the DS.RESTAdapter by making consistent use of the camelization, decamelization and pluralization methods to normalize the serialized JSON into a format that is compatible with a conventional Rails backend and Ember Data.

JSON Structure

The ActiveModelAdapter expects the JSON returned from your server to follow the REST adapter conventions substituting underscored keys for camelcased ones.

Unlike the DS.RESTAdapter, async relationship keys must be the singular form of the relationship name, followed by "_id" for DS.belongsTo relationships, or "_ids" for DS.hasMany relationships.

Conventional Names

Attribute names in your JSON payload should be the underscored versions of the attributes in your Ember.js models.

For example, if you have a Person model:

1
2
3
4
5
App.FamousPerson = DS.Model.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  occupation: DS.attr('string')
});

The JSON returned should look like this:

1
2
3
4
5
6
7
8
{
  "famous_person": {
    "id": 1,
    "first_name": "Barack",
    "last_name": "Obama",
    "occupation": "President"
  }
}

Let's imagine that Occupation is just another model:

1
2
3
4
5
6
7
8
9
10
11
App.Person = DS.Model.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  occupation: DS.belongsTo('occupation')
});

App.Occupation = DS.Model.extend({
  name: DS.attr('string'),
  salary: DS.attr('number'),
  people: DS.hasMany('person')
});

The JSON needed to avoid extra server calls, should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "people": [{
    "id": 1,
    "first_name": "Barack",
    "last_name": "Obama",
    "occupation_id": 1
  }],

  "occupations": [{
    "id": 1,
    "name": "President",
    "salary": 100000,
    "person_ids": [1]
  }]
}

Show:

Module: ember-data

By default the RESTAdapter will send each find request coming from a store.find or from accessing a relationship separately to the server. If your server supports passing ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests within a single runloop.

For example, if you have an initial payload of:

1
2
3
4
5
6
{
  post: {
    id: 1,
    comments: [1, 2]
  }
}

By default calling post.get('comments') will trigger the following requests(assuming the comments haven't been loaded before):

1
2
GET /comments/1
GET /comments/2

If you set coalesceFindRequests to true it will instead trigger the following request:

1
GET /comments?ids[]=1&ids[]=2

Setting coalesceFindRequests to true also works for store.find requests and belongsTo relationships accessed within the same runloop. If you set coalesceFindRequests: true

1
2
store.findRecord('comment', 1);
store.findRecord('comment', 2);

will also send a request to: GET /comments?ids[]=1&ids[]=2

Note: Requests coalescing rely on URL building strategy. So if you override buildURL in your app groupRecordsForFindMany more likely should be overridden as well in order for coalescing to work.

Module: ember-data

If you would like your adapter to use a custom serializer you can set the defaultSerializer property to be the name of the custom serializer.

Note the defaultSerializer serializer has a lower priority than a model specific serializer (i.e. PostSerializer) or the application serializer.

app/adapters/django.js
1
2
3
4
5
import DS from 'ember-data';

export default DS.Adapter.extend({
  defaultSerializer: 'django'
});
Module: ember-data

Some APIs require HTTP headers, e.g. to provide an API key. Arbitrary headers can be set as key/value pairs on the RESTAdapter's headers object and Ember Data will send them along with each ajax request. For dynamic headers see headers customization.

app/adapters/application.js
1
2
3
4
5
6
7
8
import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  headers: {
    "API_KEY": "secret key",
    "ANOTHER_HEADER": "Some header value"
  }
});
Module: ember-data

An adapter can target other hosts by setting the host property.

app/adapters/application.js
1
2
3
4
5
import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  host: 'https://api.example.com'
});

Requests for App.Post would now target https://api.example.com/post/.

Module: ember-data

Endpoint paths can be prefixed with a namespace by setting the namespace property on the adapter:

app/adapters/application.js
1
2
3
4
5
import DS from 'ember-data';

export default DS.RESTAdapter.extend({
  namespace: 'api/1'
});

Requests for App.Post would now target /api/1/post/.