Last reviewed in June 2019 with Ember Octane
In our previous post we loaded a subset of models from a non-standard API. As the store was populated with the response, customizing query
was appropriate.
But not every endpoint loads or saves models. We may have non-RESTful API operations such as:
PATCH /tasks/1/suspend
call that returns a booleanGET /task/suspend?id=1
call that returns an HTTP 204
no matter whatPOST /completeAllTasks
call that returns an integer with the amount of tasks marked as completedCan we still use Ember Data or are we out of luck?
Let's use the first example: suspend
must end up calling PATCH /tasks/:id/suspend
. This is how we would like to call it:
export default class SomeController extends Controller {
suspend() {
this.model.suspend();
}
}
ember-api-actions
With the help of the ember-api-actions add-on, our model could look like this:
// app/models/task.js
import DS from 'ember-data';
import { memberAction } from 'ember-api-actions';
const { Model, attr } = DS;
export default class TaskModel extends Model {
attr() name;
attr() description;
belongsTo() owner;
hasMany() tasks;
suspend: memberAction({ path: 'suspend', type: 'patch' })
});
That's it!
Need to pass parameters, like max: 9
? Piece of cake:
export default class SomeController extends Controller {
suspend() {
this.model.suspend({ max: 9 });
}
}
These params will be passed onto the XHR call.
ember-api-actions
also allows to act on collections. See an example here.
If for some reason we don't want to use this add-on, or we need to further fine-tune…
We might be inclined to think “Non-standard API? I need to make an ajax call", but that's not necessarily the best idea. We miss out on configuration parameters already available to the adapter: namespace, host, custom headers and so on.
So how do we tweak the adapter (and the model) for such a custom call?
For starters, our model will delegate the function to its adapter:
// app/models/task.js
import DS from 'ember-data';
import { memberAction } from 'ember-api-actions';
const { Model, attr } = DS;
export default class TaskModel extends Model {
attr() name;
attr() description;
belongsTo() owner;
hasMany() tasks;
suspend() {
const adapter = this.store.adapterFor('task');
return adapter.suspend(this);
}
}
Our adapter can now take advantage of adapter helpers such as buildURL
and ajax
!
// app/adapters/task.js
import ApplicationAdapter from './application';
export default class TaskAdapter extends ApplicationAdapter {
suspend(model) {
const url = this.buildURL('task', model.id) + "/suspend";
return this.ajax(url, 'PATCH');
}
}
Again, super simple!
Snacks is the best of Ember Octane in a highly digestible monthly newsletter. (No spam. EVER.)
Let's suppose we now need to hit the POST /task/suspend?id=1
endpoint, supplying the body parameter max=9
. We want to invoke it just like with the add-on:
export default class SomeController extends Controller {
suspend() {
this.model.suspend({ max: 9 });
}
}
Our suspend
function will have to be modified to accept parameters:
// app/models/task.js
import DS from 'ember-data';
import { memberAction } from 'ember-api-actions';
const { Model, attr } = DS;
export default class TaskModel extends Model {
attr() name;
attr() description;
belongsTo() owner;
hasMany() tasks;
suspend(params) {
const adapter = this.store.adapterFor('task');
return adapter.suspend(this, params);
}
}
And the adapter, in turn, could look like this:
// app/adapters/task.js
import ApplicationAdapter from './application';
export default class TaskAdapter extends ApplicationAdapter {
suspend(model, params) {
const url = `${this.buildURL('task')}/suspend?id=${model.id}`;
return this.ajax(url, 'POST', { data: params });
}
}
It does work!
This was a short introduction to customizing non-standard endpoints. Let me know what yours look like and I'll try to include them here!
Remember, if you are receiving a model (or collection of models) you could place the response into pushPayload
. But I definitely recommend customizing findRecord
, query
, etc. as discussed in this previous post.
Snacks is the best of Ember Octane in a highly digestible monthly newsletter. (No spam. EVER.)