Home > Mobile >  Defining two separate API endpoints for essentially the same logic just for the sake of avoiding RES
Defining two separate API endpoints for essentially the same logic just for the sake of avoiding RES

Time:09-17

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.

  • Related