Customizing Ember Data
Kyle Coberly
www.kylecoberly.com
Adapters
Where should data requests be sent?
Serializers
How does the data need to be formatted?
Ember Data
Included:
Adapter
JSONAdapter
RESTAdapter
JSONAPIAdapter
Included:
Serializer
JSONSerializer
RESTSerializer
JSONAPISerializer
Creating Adapters
An application adapter applies to every request in the application
ember g adapter application
ember g adapter some-model
An model-specific adapter applies to every request for one particular model
Creating Serializers
Same thing applies to serializers
ember g serializer application
ember g serializer some-model
JSON API
{
"links": {
"next": "http://example.com/articles?page[offset]=2"
},
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"date": "2/18/15"
},
"relationships": {
"author": {
//another JSON API document
}
},
"links": {
"self": "http://example.com/articles/1"
}
}],
"included": [{
//sideloaded data
}]
}
- New recommended data format for APIs
- Used internally in Ember Data
Adapter Methods
- Determines where store requests go
- Adapters should override the DS.Adapter class
- Write some kind of implementation for each
this.store.findRecord();
this.store.createRecord();
this.store.updateRecord();
this.store.deleteRecord();
this.store.findAll();
this.store.query();
this.store.findMany();
Writing an Adapter
The store proxies the request to the adapter
import DS from "ember-data";
export default DS.Adapter.extend({
findAll(store, type){
let query = {
sort: "ascending"
};
return this.ajax(this.buildURL(type.typeKey), "GET", {
data: query
});
}
});
import Ember from "ember";
export default Ember.Route.extend({
model(){
return this.store.findAll("someModel");
}
});
Overriding RESTAdapter Properties
- Can be done at the application level
- Can be overridden for each model
- Requests will start with to http://www.kylecoberly.com/api
- Headers are sent on every request
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
host: "kylecoberly.com",
namespace: "api",
headers: { //Could also be a computed property
headerName: "Header Value",
computedHeader: function(){
return "A computed value";
}
}
});
Overriding buildURL
- Can be done at the application level
- Can be overridden for each model
- Requests for "conversations" will go to http://www.kylecoberly.com/me/convos
- this._super() runs the original function
- Otherwise, build it yourself from host and namespace
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
buildURL(model, id, snapshot, requestType, query) {
let url = this._super(); //old URL
if (url.toString().contains('conversations')){
url = url.replace('conversations', 'me/convos');
} //new URL
return url;
}
});
Using serialize()
- Transforms data from the server to the data your Ember app expects
- this.serialize() proxies the request to the related serializer
- Application Adapter will use the Application Serializer, etc.
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
createRecord(model, id, snapshot, requestType, query) {
let data = this.serialize(snapshot);
return this.ajax(this.buildURL(type.typeKey), "POST", data);
}
});
Serializer Methods
- Translate from Ember to API and back again
- Serialize to format Ember data for the API
- Normalize to format for Response data for Ember
- Serializer doesn't have to match Adapter (eg. RESTAdapter with ActiveModelSerializer)
this.normalize();
this.serialize();
Writing a Serializer
- Can be application-wide or model specific
import DS from "ember-data";
export default DS.Serializer.extend({
serialize(snapshot, options){
return JSON.stringify({
title: snapshot.attr("title"),
author: snapshot.attr("author")
});
},
normalize(modelName, dataHash){
return {
data: {
id: dataHash.id,
title: dataHash.title,
author: dataHash.author
}
};
}
});
Overriding RESTSerializer Properties
- primaryKey will map its value to "id" in Ember
- attrs will map other properties
import DS from "ember-data";
export default DS.RESTSerializer.extend({
primaryKey: "_id",
attrs: {
emberName: "serverName",
otherEmberName: {
key: "otherServerName"
},
willNotBeSerialized: {
serialize: false
}
}
});
Overriding RESTSerializer Methods
- Each one starts with "normalize", ends in "Response"
- Each one maps to its store action
- Each one has the same interface
import DS from "ember-data";
export default DS.RESTSerializer.extend({
normalizeFindAllResponse(store, primaryModelClass, payload, id, requestType){
return {
data: {
id: payload.id,
title: payload.title,
author: payload.author
}
};
}
});
RESTAdapter Normalize Methods
-
normalizeResponse()
-
normalizeFindAllResponse()
-
normalizeFindRecordResponse()
-
normalizeFindManyResponse()
-
normalizeFindBelongsToResponse()
-
normalizeFindHasManyResponse()
-
normalizeQueryResponse()
-
normalizeQueryRecordResponse()
-
normalizeCreateRecordResponse()
-
normalizeDeleteRecordResponse()
-
normalizeUpdateRecordResponse()
-
normalizeSaveResponse()
-
normalizeSingleResponse()
-
normalizeArrayResponse()
Other RESTSerializer Methods
- serializeAttribute()
- serializeBelongsTo()
- serializeHasMany()
Questions?
Thank You!
www.kylecoberly.com

Getting Started With Ember
By Kyle Coberly