Imagine e.g. binding table user_tasks(user_id, task_id) with m:n relationship. I want to insert new record. The endpoint should return 204 Status No Content if the record was inserted or if such record already existed. How would you compose such endpoint?
GET users/{user_id}/tasks/{task_id}
POST users/{user_id}/tasks/{task_id}
PUT users/{user_id}/tasks/{task_id}
PUT user_tasks + payload: {"user_id": 1, "task_id": 2}
I would personally go with GET because method is idempotent (if I understand it correctly) and it does not contain payload, but I am not sure.
GET users/{user_id}/tasks/{task_id}/position/{position}
POST user_tasks + payload: {"user_id": 1, "task_id": 2, "position": 3}
POST users/{user_id}/tasks/{task_id} + payload: {"position": 3}
PUT users/{user_id}/tasks/{task_id} + payload: {"position": 3}
We have both cases in our project.
Looks like PUT
will suit your task better than GET
, simply because GET
should not modify state on the server.
From RFC 2616:
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI
Also from there:
Safe Methods: In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe"
On the other hand, for PUT:
The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.
Seems like it fits nicely, as there is mentioning of possibility that some resource already exists and we probably don't want to create it again. PUT
is also considered idempotent.
Now imagine that the table would have one other column: user_tasks(user_id, task_id, position). What is best solution here?
Best solution everywhere is to pick the approach that fits you and users of the API best. The only possible downside here could be that one of two mentioned schemes isn't similar to other schemes in the API and some users can find it unexpected.
Personally I would go with PUT users/{user_id}/tasks/{task_id}/assign?position=3
or with your approach where parameters are passed through payload. I think that the URI should be some identifiable resource, whereas user_tasks
doesn't look like one. It looks like an action name, which usually can be seen in the end of URI, for example, look how Twitter did that.