In Ember Data, an Adapter determines how data is persisted to a backend data store. Things such as the backend host, URL format and headers used to talk to a REST API can all be configured in an adapter.
Ember Data's default Adapter has some built-in assumptions about how a REST API should look. If your backend conventions differ from those assumptions, Ember Data allows either slight adjustments or you can switch to a different adapter if your backend works noticeably differently.
(If you're looking to adjust how the data sent to the backend is formatted, check the serializer page.)
Extending Adapters is a natural process in Ember Data. Ember takes the position that you should extend an adapter to add different functionality. This results in code that is more testable, easier to understand and reduces bloat for people who may want to subclass your adapter.
If your backend has some consistent rules you can define an
adapter:application
. The adapter:application
will get priority over
the default Adapter, however it will still be superseded by model
specific Adapters.
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
// Application specific overrides go here
}
If there is a model that does not follow the backend's typical conventions, you
can create an adapter that is specific to that model. The model-specific adapter
will override the rules in the application
adapter.
To create a model-specific adapter, run the command ember generate adapter <model-name>
.
For example, suppose there is a post
model that needs to talk to the v1
API
in the backend. We can run ember generate adapter post
to create the adapter,
then specify the post
adapter's namespace:
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class PostAdapter extends JSONAPIAdapter {
namespace = 'api/v1';
}
Ember Data comes with several built-in adapters. Feel free to use these adapters as a starting point for creating your own custom adapter.
Adapter
is the basic adapter with no functionality. It is generally a good starting point if you want to create an adapter that is radically different from the other Ember adapters.JSONAPIAdapter
TheJSONAPIAdapter
is the default adapter and follows JSON:API conventions to communicate with an HTTP server by transmitting JSON via XHR.RESTAdapter
TheRESTAdapter
allows your store to communicate with an HTTP server by transmitting JSON via XHR. Before Ember Data 2.0 this adapter was the default.
Customizing the JSONAPIAdapter
The JSONAPIAdapter has a handful of hooks that are commonly used to extend it to work with non-standard backends.
URL Conventions
The JSONAPIAdapter
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.findRecord('post', 1).then(function(post) {
});
The JSON:API 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 JSON:API adapter:
Action | HTTP Verb | URL |
---|---|---|
Find | GET | /posts/123 |
Find All | GET | /posts |
Update | PATCH | /posts/123 |
Create | POST | /posts |
Delete | DELETE | /posts/123 |
Pluralization Customization
To facilitate pluralizing model names when generating route URLs Ember
Data comes bundled with
Ember Inflector, an
ActiveSupport::Inflector compatible library for inflecting words
between plural and singular forms. Irregular or uncountable
pluralizations can be specified via Ember.Inflector.inflector
.
To do this, create an Initializer file containing your customizations. The Ember CLI's initializer
generator can be used ember generate initializer custom-inflector-rules
to create the file. Update its content as follows:
import Inflector from 'ember-inflector';
export function initialize(/* application */) {
const inflector = Inflector.inflector;
// Tell the inflector that the plural of "campus" is "campuses"
inflector.irregular('campus', 'campuses');
// Tell the inflector that the plural of "advice" is "advice"
inflector.uncountable('advice');
}
export default {
name: 'custom-inflector-rules',
initialize
};
The JSON:API adapter will now make requests for Campus
models to
/campuses
and /campuses/1
(instead of /campus/
and /campus/1
),
and requests for advice
to /advice
and /advice/1
(instead of
/advices/
and /advices/1
).
When specifying irregular inflection rules for compound words, only the final word or phrase should be specified. For example, to specify the plural of redCow
as redKine
or red-cow
as red-kine
, only the final word segments cow
and kine
should be specified:
inflector.irregular('cow', 'kine');
Endpoint Path Customization
The namespace
property can be used to prefix requests with a
specific URL namespace.
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
namespace = 'api/1';
}
Requests for person
would now target https://api.emberjs.com/api/1/people/1
.
Host Customization
By default, the adapter will target the current domain. If you would
like to specify a new domain you can do so by setting the host
property on the adapter.
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
host = 'https://api.example.com';
}
Requests for person
would now target https://api.example.com/people/1
.
Path Customization
By default, the JSONAPIAdapter
will attempt to pluralize and dasherize
the model name to generate the path name. If this convention does not
conform to your backend you can override the pathForType
method.
For example, if you did not want to pluralize model names and needed
underscore_case instead of dash-case you could override the
pathForType
method like this:
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import { underscore } from '@ember/string';
export default class ApplicationAdapter extends JSONAPIAdapter {
pathForType(type) {
return underscore(type);
}
}
Requests for person
would now target /person/1
.
Requests for user-profile
would now target /user_profile/1
.
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 JSONAPIAdapter
's headers
object and Ember Data will send them along with each ajax request.
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
headers = {
'API_KEY': 'secret key',
'ANOTHER_HEADER': 'Some header value'
};
}
You can combine tracked properties with ES6 getters to make headers
dynamic. For example, you may have a session
service with a tracked property called authToken
:
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import { service } from '@ember/service';
export default class ApplicationAdapter extends JSONAPIAdapter {
@service session;
get headers() {
return {
'API_KEY': this.session.authToken,
'ANOTHER_HEADER': 'Some header value'
};
}
}
Getters recompute with each
access, so you could just as easily rely upon another dynamic value such as
document.cookie
.
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import { get } from '@ember/object';
export default class ApplicationAdapter extends JSONAPIAdapter {
get headers() {
return {
'API_KEY': get(document.cookie.match(/apiKey\=([^;]*)/), '1'),
'ANOTHER_HEADER': 'Some header value'
};
}
}
Community Adapters
If none of the built-in Ember Data Adapters work for your backend, be sure to check out some of the community maintained Ember Data Adapters. Some good places to look for Ember Data Adapters include: