Reputation: 1636
We have built a RESTful API with URLs like
/api/v1/cars/
/api/v1/cars/34
All these endpoints accept GET
, PUT
, POST
and DELETE
methods as usual.
To help you understand our models: these are cars whose engine/wheels/specifications/etc are typically modified as part of a process of tuning.
We therefore have the concept of a 'report' on a car: a collection of results of various tests that are carried out on a car and stored for later viewing (just like your mechanic might produce a report on your car for later comparison).
These reports are stored at URLs like
/api/v1/car_reports/
/api/v1/car_reports/76
We have chosen to not allow these end points to accept a POST
method, you can only GET
or DELETE
them (there is no sense in a PUT
because these reports should never be modified after creation).
We made the decision that you create a car report via the URL of the car itself:
POST
to an endpoint like /api/v1/cars/34/make_report
, with a list of the tests you wish to run as the body data in this request, /api/v1/car_reports/77
./api/v1/car_reports/77
in the response to the client (and we decided, as a formality/convenience, to actually include a copy of the report in the body of the response).Our front end consumes this API with a Backbone.Model
and Backbone.Collection
like:
var Car = Backbone.Model.extend();
var CarCollection = Backbone.Collection.extend({
model: Car,
url: '/api/v1/cars'
});
var CarReport = Backbone.Model.extend();
car CarReportCollection = Backbone.Collection.extend({
model: CarReport,
url: '/api/v1/car_reports'
});
We can create a Car
easily via the CarCollection
:
var car_collection = new CarCollection();
var car = car_collection.create({
make: 'Ford',
engine_size: 1600,
tyres: 'Michelin'
});
// Suppose that car.id = 34
What is the best way to create a CarReport
on that car?
At the moment I am doing an explicit ajax call like:
var tests = {
'maximum speed',
'miles per gallon'
};
$.ajax({
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(tests),
url: car_collection.url + '/' + car.id + '/make_report'
});
This feels like a hack.
How can I do this in a more Backbone-ish way?
Many thanks.
Upvotes: 1
Views: 218
Reputation: 18859
In REST, I feel that the following:
We made the decision that you create a car report via the URL of the car itself:
- you POST to an endpoint like /api/v1/cars/34/make_report, with a list of the tests you wish to run as the body data in this request,
Is a no-no.
The endpoint is meant to represent a resource, the REST verbs (ie. GET, POST, PUT and DELETE) being the only actions to be performed on the resource.
Therefore, the make_request method you defined would rather be formulated as:
POST /api/v1/cars/34/reports
Here are my two cents on the rest of the problem:
When you define a URL as such:
/api/v1/car_reports/77
This is not wrong, but I feel that the following formulation is cleaner:
/api/v1/cars/reports/:id
It is more custom to build url's this way. If tomorrow you have motorcycles as well, you would have:
/api/v1/cars/reports/:id
/api/v1/motorcycles/reports/:id
Your question:
What is the best way to create a CarReport on that car? At the moment I am doing an explicit ajax call like:
The example you shared uses jQuery ajax directly. Backbone also depends on this method to perform ajax calls to the server, but its philosophy is a bit different. The purpose for a Backbone model is to represent a single resource unit, whereas the Backbone Collection represents a collection of units. Therefore, if you want to POST a model to the server, you are better off calling save()
directly on the model after having set its properties to be POSTed, if you want to follow the philosophy where models represent a resource unit.
However, in your example, the report isn't 'actually' directly bound to the car, as it is bound to tests that are executed on the car. So I would suggest having something like:
/api/v1/cars/77/tests/reports
The reason being, that tests are stored in a database table (many to many) where test data are linked to the cars. As such "tests" becomes the resource as a subset of a specific car where data can be sent to as a restful route (I imagine that it could theoretically be possible that you perform multiple tests on the same car, which would make the system more flexible).
Rather than defining make_report
in the url, it could be a method that is invoked when the test data are posted or updated (generating reports with v1, v2, etc).
Thinking further, if you would assume that you might have other vehicles such as motorcycles tomorrow, you might make tests a resource as well with the vehicle as a subset, which would also make sense:
/api/v1/tests/cars
/api/v1/tests/motorcycles
In the end, all of these options are possible. Therefore, REST isn't really defined as an exact science rather than an architectural style; if you implement it correctly in the backend, it will work nevertheless. So I guess that what you opt for depends on what makes the most sense to you and your specific case.
My two cents, I hope it has given some insights on further options.
Upvotes: 3