0

I'm designing a REST API in which users create groups that other users join and leave.

A user creates a group by making a request like this:

POST /groups

Request body:
{
  "name": "Avengers",
  "description": "Earth's mightiest heroes"
}

A user retrieves a group by making a request like this:

GET /groups/{id}

Response body:
{
  "name": "Avengers",
  "description": "Earth's mightiest heroes",
  "members": [
    {
      "user_name": "Thor",
      "user_id": 1
    }
  ]
}

I'm trying to figure out how I should represent a user joining and leaving a group. I could do something like POST /groups/{id}/join and POST /groups/{id}/leave, but that doesn't feel RESTful. Can someone please recommend a RESTful way to represent these actions?

3
  • 1
    It's not clear to me what the issue is - I don't see anything which obviously violates REST principles in any way. Jun 10 at 4:58
  • Fixed it: POST /groups/{id}/restfulljoin :) Jun 10 at 6:46
  • Assuming the acction is performed by an authenticated and authorized user, then Why not Joining a group by PUT /me/groups/{gid} and leaving a group by DELETE /me/groups/{gid}. Doing this through /groups resource seems a management task (e.g admins removing users from groups) rather than someone leaving or joining a group.
    – Laiv
    Jun 11 at 10:58

3 Answers 3

2

REST does not provide an all-emcompassing scripture that answers every possible use case. There are many ways to skin this particular cat.

You could have a membership resource that describes a single user's joining of a single group. The REST implementation would then be a simple CRUD on the /memberships route.

But you could also define memberships as a combined reference to a group and a person, e.g. /groups/{groupId}/members/{memberId}, or the other way around. I often prefer implementing both and mapping them to the same backend logic.

REST is not an all-encompassing scripture. You should look at what makes the most sense for your use case and then use that.

2
  • I’ve seen the second approach referred to as a sub-resource. I think that approach makes sense here. Let’s say that I want to return the full group when someone makes a POST or DELETE request to /groups/{groupId}/members/{memberId}. Is that still considered RESTful? (Or maybe I shouldn’t worry too much based on the first part of your response :) ).
    – Matt
    Jun 10 at 20:56
  • @Matt Whether you return a resource at the end of a command is something you have to decide based on what makes sense to you, not because REST tells you to or not. However, I would not return a full group after modifying a single membership (regardless of taking approach 1 or 2, as it still expresses a membership in either case). Those are two different resources and they should live on their own resource endpoints.
    – Flater
    Jun 11 at 10:26
1

Can someone please recommend a RESTful way to represent these actions?

Any of these might be reasonable:

POST /groups/{id}
PUT /groups/{id}
PATCH /groups/{id}

The target URI is the same that we use for the GET request, because we want general purpose HTTP caches to invalidate previously cached responses when we make a change. See RFC 9111, Section 4.4


On the web, we normally see HTML forms being used to send information to the server, so we would expect to use POST with some application/x-www-form-urlencoded payload, which might look like

POST /groups/{id}
Content-Type: application/x-www-form-urlencoded

action=addMember&user_name=Squirrel%20Girl

In other contexts, it makes sense for the client to edit a local copy of the resource, and then send the results to the server. So you might see requests like

PUT /groups/{id}
Content-Type: application/json

{
  "name": "Avengers",
  "description": "Earth's mightiest heroes",
  "members": [
    {
      "user_name": "Thor",
      "user_id": 1
    },
    {
      "user_name": "Squirrel Girl",
      "user_id": 2
    }
  ]
}

PUT requests, in effect, "make your copy of this resource look like the copy I have enclosed" -- but the server is allowed to make its own changes to its own resource. For example, it might be that the server chooses a different user_id for the new members.

PATCH is like PUT, in the sense that we are telling the server how to change its copy to match ours; the difference being that we are sending just a representation of our changes, rather than the entire document.

PATCH /groups/{id}
Content-Type: application/json-patch+json

[
  { "op": "add", "path": "/members/1", "value": {
      "user_name": "Squirrel Girl",
      "user_id": 2
} }
]

Normally, in places where PATCH makes sense, you would also support PUT, and permit the client to choose whether to send the entire representation (PUT) or just the changes (PATCH).


The important idea here is that HTTP standardizes retrieval and manipulation of documents. HTTP methods work the same way whether the document in question is a roster of the worlds mightiest heroes or a sandwich menu from the local shop.

HTTP methods, resource identifiers, fields, and so on are of the "retrieval and manipulation of documents" domain; the work of designing a good API is in designing the right documents, and linking them properly.

-1
POST /groups/{id}/members
DELETE /groups/{id}/members/{id}
2
  • maybe a comment to go along with the downvote? Jun 10 at 9:12
  • While I think that the suggestion implied by this answer is reasonable, it would be a much better answer if it also explains the idea using actual words. For example, see Flater's answer that also discusses part of your idea.
    – amon
    Jun 10 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.