Tools (adapters and serializers)
To connect to an API that is not compatible with Ember-Data (e.g. tumblr), you must configure an adapter such as RestAdapter or JSONAPIAdapter to create the request properly.
(JSONAPIAdapter is a subclass of RESTAdapter that adjusts some things for the JSON API, such as the Accept: application/vnd.api+json header required by the specifications)
In addition, if the extracted data does not conform to the Ember Data JSON JSON API , you must configure a serializer , such as JSONSerializer , RESTSerializer or JSONAPISerializer , to format and massage the data, choosing the most suitable serializer for the data returned by your server. Here is a brief overview of these 3 serializers:
JSONSerializer
- Base serializer (extends abstract DS.Serializer)
- Provides normalization hooks for processing received data.
- Provides serialization hooks for massaging sent data.
Expects plain JSON format
{
"id": 1,
"name": "Bob",
"friends": [array of friend ids],
"links": {
"home": "/ bobshome"
}
}
RESTSerializer
- Extends JSONSerializer
Expects a similar JSON format (with "root"):
{
"user": {
"id": 1,
"name": "Bob",
"friends": [array of friend ids],
"links": {
"home": "/ bobshome"
}
}
}
JSONAPISerializer
In general, we can mix and match adapters and serializers with what is best for our data and URL endpoint structure. Each of them can be applied for each application or for each model.
Methods
When creating a query, depending on the exact behavior you need, you must override the corresponding hooks provided by the Ember data store (in the adapter):

Each of the above hooks has a corresponding normalize{{HOOK_NAME}}Response method in the serializer (for example, normalizeFindRecordResponse ), where the data can be massed depending on the request method (which we call):
<strong> Examples
Let's say we want a Bob Tumblr blog called "mynameisbob".
Here is a general example of how to do this:
export default DS.RESTAdapter.extend({ namespace: 'v2/blog/', // https://guides.emberjs.com/v2.0.0/models/customizing-adapters/
Usage in Route :
// ... model() { return this.store.findRecord('blog', 'mynameisbob'); } // ...
However, there are several ways to do this. You can also save the api key and host URL as the properties of your adapter and simply use them to create the URL (using the buildURL hook):
export default DS.RESTAdapter.extend({ hostUrl: 'https://api.tumblr.com/v2/blog' apiKey: 'abcdefg', buildURL: function(modelName, id, snapshot, requestType, query) { // customize the url based on the parameters // lets assume the id is the blog name return `${this.get('hostUrl')}/${id}.tumblr.com/posts?api_key=${this.get('apiKey')}`; } // find a blog findRecord: function(store, type, id, snapshot) { const URL = this.buildURL(type.modelName, id, snapshot, 'findRecord'); return this.ajax(URL, 'GET'); } });
Here's the Github Repo for a simple Ember app communicating with the Github API
Adapters connected to the community to serve as examples:
Some useful indications:
Pre Ember 1.13.0
Here is a simple example of how to write a custom RestAdapter . Essentially, you need to rewrite the hooks you need to search for the store ( find , findAll , findQuery ...) as well as buildURL() .
Since this is an external API and we need to worry about CORS, we also need to override the ajax hook.
Custom Tumblr Adapter:
App.TumblrAdapter = DS.RESTAdapter.extend({ buildURL: function(type, id, record) { var tumblrBlogName = 'feministlibraryonwheels'; var tumblrApiKey = 'abcdefg'; var tumblrRequestUrl = 'http://api.tumblr.com/v2/blog/' + tumblrBlogName + '.tumblr.com' + '/posts?api_key=' + tumblrApiKey; return tumblrRequestUrl; }, ajax: function(url, method, hash) { hash = hash || {}; // hash may be undefined hash.crossDomain = true; // make it CORS hash.xhrFields = {withCredentials: true}; return this._super(url, method, hash); }, find: function(store, type, id, record) { // customization here or within buildURL return this.ajax(this.buildURL(), 'GET'); }, findAll: function(store, type, sinceToken) { // customization here or within buildURL return this.ajax(this.buildURL(), 'GET'); }, findQuery: function(store, type, query) { // customization here or within buildURL return this.ajax(this.buildURL(), 'GET'); }, findMany: function(store, type, ids, owner) { // customization here or within buildURL return this.ajax(this.buildURL(), 'GET'); } });
If the answer is incorrectly formatted for Ember Data, we can quickly fix it in ajax with a simple promise:
ajax: function(url, method, hash) { hash = hash || {}; // hash may be undefined hash.crossDomain = true; // make it CORS hash.xhrFields = {withCredentials: true}; return this._super(url, method, hash).then(function(json) { // Massage data to look like RESTAdapter expects. return { tumblrs: [json] }; }); },
If you want to correctly identify all models with a relationship, you will need to run a custom RESTSerializer to properly massage the data.
Here is a simple jsbin example I came across while researching these things myself.