1

I want to design my rest endpoint with the appropriate method for the following scenario.

There is a group. Each group has members. A member has to be approved by the group admin in order to become a member. If the admin rejects, the user cannot become a member of the group.

I have the following endpoints to address this scenario.

  1. When a user joins a group POST /projects/api/v1/projects/{project id}/members/{member id}

  2. For approving membership PUT /groups/api/v1/groups/{group id}/members/{member id}/approve to approve membership

However, I am having trouble deciding the right endpoint for rejecting membership. Should I use

PUT   /projects/api/v1/projects/{project id}/members/{member id}/reject

or

DELETE  /projects/api/v1/projects/{project id}/members/{member id}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
java_geek
  • 17,585
  • 30
  • 91
  • 113

3 Answers3

5

To put it bluntly, you are using URIs wrong. 'actions' such as 'approve' should not be part of the URI.

Multiple lists

I would imagine that an 'obvious' way of doing this is for a group to have a list both of the approved members, and those awaiting to be accepted. If a user wants to be added, he might POST /groups/{group id}/waitlist/{user id}. This makes it very easy for an admin to reject, they can just DELETE /groups/{group id}/wait_list/{user id}. If the admin wishes to approve, he could POST /groups/{group id}/users/{user id}.

Now, one of the problems you might see here is that we know have the user in both the 'approved users' list and the 'user awaiting approval' list. Depending on how exactly you manage this users lists, that might not be a problem. If though, you wished that the a user would only ever be in one of these lists, you will have to remove them the wait list, after approval.

A simply thing to do at first glance. Two options come to mind, one would be that when you approve the user, by adding them to the 'users' list, the server ALSO deletes them from the 'waitlist'. This is a bit of a dirty thing to do, POST is not meant to have side effects like that. So really, we need the client (the admin) to make a second request.

PATCH People!

Of course, this could all be made much easier if use the PATCH method. A group can have a list of users, paired with their status in this group. When you want to be added to a group, you make a request like PATCH /group/{group id}/users/ {'user id': 666, 'status':'request access'}. When an admin approves/rejects, they make a nearly identical request, just updating the status field.

The added benefit here, the admin could set the user's status to 'rejected', 'revoked', 'suspended' etc. and nothing specially really needs to happen. Though you do need to ensure your server is validating these selections, else you end up with all sorts of silliness. You can also allow an admin to add a user directly, without the user having to request permissions first. Such as if an admin wanted to add someone quickly as a moderator.

thecoshman
  • 8,394
  • 8
  • 55
  • 77
  • The concern with using PATCH is, what is the support available in different browsers to make PATCH calls? The application supports various browsers, so browser compatibility is an issue. – java_geek Jun 17 '14 at 05:46
  • 1
    I'm sorry, are we talking websites or REST apis? Besides, you can [use PATCH via JS](http://stackoverflow.com/questions/7502278/can-you-make-a-http-patch-request-from-javascript). Besides, if you are using shoddy tools, you can always have the user/client GET the resource, trust them to modify it correctly, PUT it back; then verify the changes because you can't trust them silly users to do things right. Keep in mind, there is nothing special about the 'methods' you use in a HTTP request, it's just a string. You could use a `SLAP` METHOD if you wanted to. – thecoshman Jun 17 '14 at 09:41
0

If you need to change the existing object resource (changing the status as well) you have to use PUT. It overwrites the existing object resource and returns 200 success status. POST is recommended for sending data to the server to be processed. Furthermore, to apply conditional behaviour to select the exact resource can use If-Match header.

Find a better guide here.

malintha
  • 176
  • 3
  • 18
  • The server can process PUT too... POST is, roughly speaking, for when you want the server to decide where to store the resource. – thecoshman Jun 15 '14 at 20:26
  • Considering the request URI, there is no such difference between two. But the RFC specifies, "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." – malintha Jun 15 '14 at 23:26
  • I'm not sure what your point is here. You answer said that POST is recommended for sending data to be processed. I simply pointed out that you can do the same with PUT too. POST just doesn't tie the data to any URI, it lets the server decide. The server can process a users PUT request, such as `PUT /users/thecoshman` and reject the data, or perform calculations based on it. You do not have to blindly accept the data sent your server, though obviously such changes should be kept to a minimum. – thecoshman Jun 16 '14 at 08:33
-1

I would use POST. You could then put the member in a rejected state for that group. Otherwise I guess the member could just reapply again and again if the resource is deleted.

To illustrate:

POST   /projects/api/v1/projects/1/members

201, CREATED

{
    id: 1234
    member: 'Tom'
    status: 'pending'
    links: [{
            rel:  'self',
            method: 'GET',
            href: '/projects/api/v1/projects/1/members/1234'
        },{
            rel:  'member',
            method: 'GET',
            href: '/projects/api/v1/members/4321'
        },{
            rel:  'reject',
            method: 'POST',
            href: '/projects/api/v1/projects/1/members/1234/reject'
        },{
            rel:  'accept',
            method: 'POST',
            href: '/projects/api/v1/projects/1/members/1234/accept'
        }]
}


POST   /projects/api/v1/projects/1/members/1234/reject

204: No Content


GET   /projects/api/v1/projects/1/members/1234

200, OK

{
    id: 1234
    member: 'Tom'
    status: 'rejected'
    links: [{
            rel:  'self',
            method: 'GET',
            href: '/projects/api/v1/projects/1/members/1234'
        },{
            rel:  'member',
            method: 'GET',
            href: '/projects/api/v1/members/4321'
        },{
            rel:  'appeal',
            method: 'POST',
            href: '/projects/api/v1/projects/1/members/1234/appeal'
        }]
}

EDIT: Then of course the reject and accept action links are only provided for the user with correct permission.

Civing
  • 353
  • 3
  • 12
  • 'reject' and 'accept' are actions, and should therefore not be seen in the URI. – thecoshman Jun 15 '14 at 20:25
  • What do you mean? Reject and accept action URI will only be visible to users with "admin" permissions – Civing Jun 15 '14 at 21:16
  • 1
    Doesn't matter, URI should by akin to 'things'. Only the HTTP method should carry 'verb' like meaning. – thecoshman Jun 16 '14 at 08:29
  • I yield, don't have actions in your URI. Create a new resource for handling pending group membership queries. Just like the one @thecoshman suggested. – Civing Jun 16 '14 at 23:56