home
  • Blog
1.12
  • Getting Started
  • Concepts
  • The Object Model
  • Application
  • Templates
  • Routing
  • Components
  • Controllers
  • Models
    • Introduction
    • Defining Models
    • Creating and Deleting Records
    • Pushing Records into the Store
    • Persisting Records
    • Finding Records
    • Working with Records
    • The Rest Adapter
    • Connecting to an HTTP Server
    • Handling Metadata
    • Customizing Adapters
    • Frequently Asked Questions
  • Views
  • Enumerables
  • Testing
  • Configuring Ember.js
  • Ember Inspector
  • Cookbook
  • Understanding Ember.js
  • Contributing to Ember.js
Old Guides - You are viewing the guides for Ember v1.12.0.
Go to v5.0.0

The Rest Adapter

Edit pencil

By default, your store will use DS.RESTAdapter to load and save records. The RESTAdapter assumes that the URLs and JSON associated with each model are conventional; this means that, if you follow the rules, you will not need to configure the adapter or write any code in order to get started.

URL Conventions

The REST adapter is smart enough to determine the URLs it communicates with based on the name of the model. For example, if you ask for a Post by ID:

store.find('post', 1).then(function(post) {
});

The REST adapter will automatically send a GET request to /posts/1.

The actions you can take on a record map onto the following URLs in the REST adapter:

ActionHTTP VerbURL
FindGET/people/123
Find AllGET/people
UpdatePUT/people/123
CreatePOST/people
DeleteDELETE/people/123

Pluralization Customization

Irregular or uncountable pluralizations can be specified via Ember.Inflector.inflector:

var inflector = Ember.Inflector.inflector;

inflector.irregular('formula', 'formulae');
inflector.uncountable('advice');

This will tell the REST adapter that requests for formula should go to /formulae/1 instead of /formulas/1.

Endpoint Path Customization

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

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

Requests for person would now target https://api.emberjs.com/1/people/1.

Host Customization

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

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

Requests for person would now target https://api.example.com/people/1.

JSON Conventions

When requesting a record, the REST adapter expects your server to return a JSON representation of the record that conforms to the following conventions.

JSON Root

The primary record being returned should be in a named root. For example, if you request a record from /people/123, the response should be nested inside a property called person:

{
  "person": {
    "firstName": "Jeff",
    "lastName": "Atwood"
  }
}

After destroyRecord or after deleteRecord and save, the adapter expects the server to return an empty object ({}).

If you don't have the option to change the data that the server responds with, you can override the DS.JSONSerializer#extractDeleteRecord, like so:

extractDeleteRecord: function(store, type, payload) {
  // If the payload is {delete: true}, Ember Data will try to set
  // the new properties. Return null so it doesn't try to do that.
  return null;
}

Attribute Names

Attribute names should be camelized. For example, if you have a model like this:

app/models/person.js
export default DS.Model.extend({
  firstName: DS.attr('string'),
  lastName:  DS.attr('string'),

  isPersonOfTheYear: DS.attr('boolean')
});

The JSON returned from your server should look like this:

{
  "person": {
    "firstName": "Barack",
    "lastName": "Obama",
    "isPersonOfTheYear": true
  }
}

Irregular keys can be mapped with a custom serializer. If the JSON for person has a key of lastNameOfPerson, and the desired attribute name is simply lastName, then create a custom Serializer for the model and override the normalizeHash property.

app/models/person.js
export default DS.Model.extend({
  lastName: DS.attr('string')
});
app/serializers/person.js
export default DS.RESTSerializer.extend({
  normalizeHash: {
    lastNameOfPerson: function(hash) {
      hash.lastName = hash.lastNameOfPerson;
      delete hash.lastNameOfPerson;

      return hash;
    }
  }
});

Relationships

References to other records should be done by ID. For example, if you have a model with a hasMany relationship:

app/models/post.js
export default DS.Model.extend({
  comments: DS.hasMany('comment', { async: true })
});

The JSON should encode the relationship as an array of IDs:

{
  "post": {
    "comments": [1, 2, 3]
  }
}

Comments for a post can be loaded by post.get('comments'). The REST adapter will send a GET request to /comments?ids[]=1&ids[]=2&ids[]=3.

Any belongsTo relationships in the JSON representation should be the camelized version of the Ember Data model's name. For example, if you have a model:

app/models/comment.js
export default DS.Model.extend({
  post: DS.belongsTo('post')
});

The JSON should encode the relationship as an ID to another record:

{
  "comment": {
    "post": 1
  }
}

If needed these naming conventions can be overwritten by implementing the keyForRelationship method.

app/serializers/application.js
export default DS.RESTSerializer.extend({
  keyForRelationship: function(key, relationship) {
    return key + 'Ids';
  }
});

Sideloaded Relationships

To reduce the number of HTTP requests necessary, you can sideload additional records in your JSON response. Sideloaded records live outside the JSON root, and are represented as an array of hashes:

{
  "post": {
    "id": 1,
    "title": "Node is not omakase",
    "comments": [1, 2, 3]
  },

  "comments": [{
    "id": 1,
    "body": "But is it _lightweight_ omakase?"
  },
  {
    "id": 2,
    "body": "I for one welcome our new omakase overlords"
  },
  {
    "id": 3,
    "body": "Put me on the fast track to a delicious dinner"
  }]
}

Creating Custom Transformations

In some circumstances, the built in attribute types of string, number, boolean, and date may be inadequate. For example, a server may return a non-standard date format.

Ember Data can have new JSON transforms registered for use as attributes:

app/transforms/coordinate-point.js
export default DS.Transform.extend({
  serialize: function(value) {
    return [value.get('x'), value.get('y')];
  },
  deserialize: function(value) {
    return Ember.create({ x: value[0], y: value[1] });
  }
});
app/models/cursor.js
export default DS.Model.extend({
  position: DS.attr('coordinatePoint')
});

When coordinatePoint is received from the API, it is expected to be an array:

{
  cursor: {
    position: [4,9]
  }
}

But once loaded on a model instance, it will behave as an object:

var cursor = App.Cursor.find(1);
cursor.get('position.x'); //=> 4
cursor.get('position.y'); //=> 9

If position is modified and saved, it will pass through the serialize function in the transform and again be presented as an array in JSON.

left arrow
Working with Records
Connecting to an HTTP Server
right arrow
On this page

  • URL Conventions
  • Pluralization Customization
  • Endpoint Path Customization
  • Host Customization
  • JSON Conventions
  • JSON Root
  • Attribute Names
  • Relationships
  • Sideloaded Relationships
  • Creating Custom Transformations
Team Sponsors Security Legal Branding Community Guidelines
Twitter GitHub Discord Mastodon

If you want help you can contact us by email, open an issue, or get realtime help by joining the Ember Discord.

© Copyright 2023 - Tilde Inc.
Ember.js is free, open source and always will be.


Ember is generously supported by
blue