Suppose we have a M:M relationship between a User
model and a Project
model in a project management SaaS. Naturally we have a pivot table mapping users to projects. An entry is deleted either if the user leaves or if the project manager removes that user from the project.
Even though the final result is essentially the same (i.e., the entry is deleted), I imagine it would be better to distinguish between these two actions by defining two separate API endpoints as follows:
user leaves project
route DELETE /users/{id}/projects/{id}
action UserProjectController@destroy($id)
where $id
refers to the project
project manager removes user
route DELETE /projects/{id}/participants/{id}
action ProjectParticipantController@destroy($id)
where $id
refers to the user
Should I ignore Cruddy by Design and RESTful design principles and simply define leave()
join()
remove()
actions instead and use verbs in the URIs? like so:
POST /projects/{id}/join
POST /projects/{id}/leave
POST /projects/{id}/participants/{id}/remove
CodePudding user response:
join
,leave
,remove
are arguably RPC rather than REST. Pivot tables etc are how the domain is implemented and are irrelevant to the caller of the API, which works at the domain level. How do the URLs map to your domain model?
If Project
has one or more User
why not just use
/projects/{id}/
to manage everything about Project
and Participant
instances?
User
is in a Project
but only if
/projects/{id}/participants/{id}
has been called.
FWIW,
DELETE /users/{id}/projects/{id}
looks like it's deleting the Project
instance owned by a User
as the Project
object has participants
but the User
doesn't use that terminology. Perhaps it would be:
DELETE /users/{id}/projects/{id}/participants
for consistency with the domain model.
However it seems much easier to just use Project
to manage Project
instances. Removing a User
from a Project
using the User
API seems a convenience that complicates the backend and doesn't really match the Project
terminology. That would mean only having UserController
and ProjectController
classes.
You would have to consider whether a Participant
id
is the same as a User
id
and whether the pivot table handles that but that won't affect the API. The API should be an intuitive representation of your domain model.
CodePudding user response:
Something to consider: how would you do it on the web?
Even though the final result is essentially the same (i.e., the entry is deleted), I imagine it would be better to distinguish between these two actions by defining two separate API endpoints
So on the web, you might well have two different forms that are used to "achieve the same result", but that doesn't necessarily mean that the forms should target different resources.
POST /foo
Content-Type: application/x-www-form-urlencoded
action=UnsubscribeUser&otherArguments=....
POST /foo
Content-Type: application/x-www-form-urlencoded
action=CancelProject&otherArguments=....
"One resource or two resources" isn't a question of right or wrong, but rather a question of trade-offs.
POST /projects/{id}/join
POST /projects/{id}/leave
POST /projects/{id}/participants/{id}/remove
That's also "fine"; again, it's a question of trade offs. The machines don't care that your identifiers have a verb in the URI.
They do, somewhat, care whether or not the identifier for reading is the same as the identifier for writing. See RFC 7234.
But it's perfectly reasonable to say that having semantically significant URI spellings in the access log or browser history are more important to our long term success than cache-invalidation.
Keep firmly in mind that DELETE - in the context of HTTP - does not mean the same thing as DELETE in the context of SQL; we're talking about two different name spaces with their own semantics.
HTTP DELETE is of the transfer of documents over a network domain. The fact that the implementation of your handler includes a SQL DELETE is completely irrelevant.
Relatively few resources allow the DELETE method -- its primary use is for remote authoring environments, where the user has some direction regarding its effect.
It is OK to use POST.