Class RESTAdapter
public⚠️ This is LEGACY documentation for a feature that is no longer encouraged to be used. If starting a new app or thinking of implementing a new adapter, consider writing a Handler instead to be used with the RequestManager
The REST adapter allows your store to communicate with an HTTP server by transmitting JSON via XHR.
This adapter is designed around the idea that the JSON exchanged with the server should be conventional. It builds URLs in a manner that follows the structure of most common REST-style web services.
## Success and failure
The REST adapter will consider a success any response with a status code of the 2xx family ("Success"), as well as 304 ("Not Modified"). Any other status code will be considered a failure.
On success, the request promise will be resolved with the full response payload.
Failed responses with status code 422 ("Unprocessable Entity") will be
considered "invalid". The response will be discarded, except for the
errors
key. The request promise will be rejected with a InvalidError
.
This error object will encapsulate the saved errors
value.
Any other status codes will be treated as an "adapter error". The request
promise will be rejected, similarly to the "invalid" case, but with
an instance of AdapterError
instead.
## JSON Structure
The REST adapter expects the JSON returned from your server to follow these conventions.
### Object Root
The JSON payload should be an object that contains the record inside a
root property. For example, in response to a GET
request for
/posts/1
, the JSON should look like this:
{
"posts": {
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
}
}
Similarly, in response to a GET
request for /posts
, the JSON should
look like this:
{
"posts": [
{
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
},
{
"id": 2,
"title": "Rails is omakase",
"author": "D2H"
}
]
}
Note that the object root can be pluralized for both a single-object response
and an array response: the REST adapter is not strict on this. Further, if the
HTTP server responds to a GET
request to /posts/1
(e.g. the response to a
findRecord
query) with more than one object in the array, Ember Data will
only display the object with the matching ID.
### Conventional Names
Attribute names in your JSON payload should be the camelCased versions of the attributes in your Ember.js models.
For example, if you have a Person
model:
import Model, { attr } from '@ember-data/model';
export default Model.extend({
firstName: attr('string'),
lastName: attr('string'),
occupation: attr('string')
});
The JSON returned should look like this:
{
"people": {
"id": 5,
"firstName": "Zaphod",
"lastName": "Beeblebrox",
"occupation": "President"
}
}
#### Relationships
Relationships are usually represented by ids to the record in the relationship. The related records can then be sideloaded in the response under a key for the type.
{
"posts": {
"id": 5,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz",
"comments": [1, 2]
},
"comments": [{
"id": 1,
"author": "User 1",
"message": "First!",
}, {
"id": 2,
"author": "User 2",
"message": "Good Luck!",
}]
}
If the records in the relationship are not known when the response
is serialized it's also possible to represent the relationship as a
URL using the links
key in the response. Ember Data will fetch
this URL to resolve the relationship when it is accessed for the
first time.
{
"posts": {
"id": 5,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz",
"links": {
"comments": "/posts/5/comments"
}
}
}
### Errors
If a response is considered a failure, the JSON payload is expected to include
a top-level key errors
, detailing any specific issues. For example:
{
"errors": {
"msg": "Something went wrong"
}
}
This adapter does not make any assumptions as to the format of the errors
object. It will simply be passed along as is, wrapped in an instance
of InvalidError
or AdapterError
. The serializer can interpret it
afterwards.
## Customization
### Endpoint path customization
Endpoint paths can be prefixed with a namespace
by setting the namespace
property on the adapter:
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
namespace = 'api/1';
}
Requests for the Person
model would now target /api/1/people/1
.
### Host customization
An adapter can target other hosts by setting the host
property.
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
host = 'https://api.example.com';
}
### Headers customization
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.
import RESTAdapter from '@ember-data/adapter/rest';
import { computed } from '@ember/object';
export default class ApplicationAdapter extends RESTAdapter {
headers: computed(function() {
return {
'API_KEY': 'secret key',
'ANOTHER_HEADER': 'Some header value'
};
}
}
headers
can also be used as a computed property to support dynamic
headers. In the example below, the session
object has been
injected into an adapter by Ember's container.
import RESTAdapter from '@ember-data/adapter/rest';
import { computed } from '@ember/object';
export default class ApplicationAdapter extends RESTAdapter {
headers: computed('session.authToken', function() {
return {
'API_KEY': this.session.authToken,
'ANOTHER_HEADER': 'Some header value'
};
})
}
In some cases, your dynamic headers may require data from some
object outside of Ember's observer system (for example
document.cookie
). You can use the
volatile
function to set the property into a non-cached mode causing the headers to
be recomputed with every request.
import RESTAdapter from '@ember-data/adapter/rest';
import { computed } from '@ember/object';
export default class ApplicationAdapter extends RESTAdapter {
headers: computed(function() {
return {
'API_KEY': document.cookie.match(/apiKey\=([^;]*)/)['1'],
'ANOTHER_HEADER': 'Some header value'
};
}).volatile()
}
coalesceFindRequests public
Inherited from Adapter ../packages/adapter/src/index.ts:646
By default the store will try to coalesce all findRecord
calls within the same runloop
into as few requests as possible by calling groupRecordsForFindMany and passing it into a findMany call.
You can opt out of this behaviour by either not implementing the findMany hook or by setting
coalesceFindRequests to false.
headers public
Defined in ../packages/adapter/src/rest.ts:525
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.
import RESTAdapter from '@ember-data/adapter/rest';
import { computed } from '@ember/object';
export default class ApplicationAdapter extends RESTAdapter {
headers: computed(function() {
return {
'API_KEY': 'secret key',
'ANOTHER_HEADER': 'Some header value'
};
})
}
host public
Defined in ../packages/adapter/src/rest.ts:507
An adapter can target other hosts by setting the host
property.
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
host = 'https://api.example.com';
}
Requests for the Post
model would now target https://api.example.com/post/
.
namespace public
Defined in ../packages/adapter/src/rest.ts:488
Endpoint paths can be prefixed with a namespace
by setting the namespace
property on the adapter:
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
namespace = 'api/1';
}
Requests for the Post
model would now target /api/1/post/
.
useFetch public
Defined in ../packages/adapter/src/rest.ts:343
This property allows ajax to still be used instead when false
.