The model could then call store and adapter as it does on save. Also it should return a promise similar to save. I think it would also be nice to update the model with the response data from the server, if there is any. My opinion is, that it should be implemented as separate library at the moment.
A first problem I discovered, is that some methods needed for that, are not public.
I would like to hear your opinions and hints about it. Maybe someone has interest to contribute on starting a project to create an extension for ember-data.
Hi,
I think this is wrong approach to REST. What you trying to do is RPC its not REST service.
Here you could find more about RESTfull services Roy Thomas Fielding rest dissertation
Why, in my example above is upvote a nested resource for place . Now I POST to route /api/v1/places/1/upvote, which means: ‘create a new resource upvote for place with id=1’.
Yeah, this is RPC, but don’t let that stop you, There are some good use cases for it. Like sending an email or completing a purchase, where you’d like to explicitly tell the server to do something more than save the data. But I’d stick with REST for your rating example.
It seems fairly straightforward to add it. Maybe copy Store.adapterFor into your own code.
Actually, I don’t think “upvote” is a nested resource. It should be a custom action. My opinion is even REST API should not stop you from creating a few custom actions. In real world sometimes we need some custom actions to do specific things but not just save the resource. And I think custom action also makes back-end API design and test easier.
In fact you can also consider voting things as nested resource. But then the recommend way is something like this:
GET /api/v1/places/1/votes
POST /api/v1/places/1/votes # upvote
DELETE /api/v1/places/1/votes # downvote
For your use case I think one custom action is just ok:
POST /api/v1/places/1/vote
DELETE /api/v1/places/1/vote
Or you can just use the default REST actions:
PUT /api/v1/places?vote=1
PUT /api/v1/places?vote=-1
But I think it does more than one thing in a single action, which is not a good solution.
I’m not familiar with Ember Data. I guess to make custom action works you need to add methods for model, store and adapter. Because model.save calls store.scheduleSave. I think it won’t be a very complex task. We just need store to have a new API, and let adapter to build the url for custom actions. All serialize/deserialize things will be the same.
I think it’s kind of overkill. Thats easily could be done with simple REST no need for some custom actions over rest. If you need to add server side logic to voting, you should attach event listener to your ‘save’ database model method(I assume that you use some kind of ORM e.g. AR on your server side). Or you could use triggers in your database(if possible). Just KISs.
@BFalkner: Thanks for showing your solution. The problem is, that you didn’t always want to have the same model structure on the front end as on the back end. With your solution you end up with hundreds of Upvote instances, that in the end are only used to count them up, to show the overall number of up-votes. But the aggregation should be done by the database.
@alekso: Yes, it’s possible to do it with REST, but then you have to store the whole model instance only to up- or down-vote this model. Another possible solution and maybe more ‘emberish’ way is to update the model instance model.incrementProperty('votes') and to let the adapter decide how to call the server. But then you always have to implement a custom ModelAdapter with custom logic.
I don’t know if is the best approach but, I have a similar case and I try with the code below and works fine, (maybe the path or another improves could be implemented), any feedback is welcome
Thanks amk, it works great. The only trouble I have is that it doesn’t pickup the validation errors if the server returns 422. Normally, when calling save on the record, it works fine. Any tips on how to trigger the same behaviour? I tried to look around the ember-data source code but didn’t figure it out. Thanks!