1

I am designing REST API for users and teams of users. One user can belong to multiple teams . One of the requirements is to be able to sort the users belonging to team in alphabetical order. Teams can be huge and contain a lot of users.

We came with the idea to have sth like that:

POST /users/{user-uuid}/teams-membership - add user to a group in other words, create membership

DELETE /users/{user-uuid}/teams-membership/{teams-membership-uuid} - delete user membership

DELETE /users/{user-uuid}/teams-membership?team-uuid={team-uuid} - delete user membership related to the specific team uuid

POST /teams - create team and all other methods like GET for retrieving details about the team etc..

thanks to that I can achieve getting users in alphabetical order: GET /users?team-uuid={team-uuid}&sort=-name

Is there anything wrong with the design here? I am storing and querying the information about user membership on the user side and I am not confident if there is a good approach here. From pragmatic point of view it solves my "current" use-case and be extendable in the future.

2
  • Doesn't look inherently wrong. But I'd consider creating membership via PUT /users/{user}/teams/{team} since the ID of the resource is already known. In any case, style of URL structure is not essential for a REST API. While I might prefer more information in paths like GET /teams/{id}/members, using query params like GET /users?team={id} is equally good (and might be preferable if you want to offer multiple filtering, searching, and sorting options in one endpoint).
    – amon
    Commented Nov 27, 2023 at 18:13
  • The resource identifiers seem perfectly simple and logical, if that's the question. I'm not too sure why sorting would be affected by the many-to-many side of things, looks like a separate issue. Commented Nov 28, 2023 at 9:36

1 Answer 1

1

You have defined that the relationship between user and team is many-to-many (assuming a team can have more than one member).

You are exposing what is most likely a link table due to the teams-membership-uuid, there are some valid reasons for doing this - for example you wish to expose the date that a user joined a team (which would typically be a create_timestamp on the link table. That said this particular field could also be exposed as part of the result of querying either: the current members of a team or the current teams of a user. The crux of the question I am asking is, do you have use cases where the link between the two tables is an entity in its own right, i.e. you have three entities:

  • Users
  • Teams
  • Team-Memberships

If you have three entities you should model your API with those three entities.

Note: I don't consider a "pure join" to be an entity, there would have to be some additional functionality required for example: complex auditing or permissions within the team.


If you only have two entities you should not leak the internal implementation detail of the UUID of the link table.

Additionally I would like to formalize Amon's comment and say that it is common in many-to-many relationships to have queries that start on both sides of the relationship (list of teams a user is in, list of users in a team - in this case) so it makes sense to define query operations on both sides.

With a pure many-to-many link table, there are only two mutating actions (link or unlink) as such you can define these actions on either side of the API (or theoretically both), I personally prefer to put the mutating actions on the team side. My rational is that if there was no concept of team membership the concept of teams probably wouldn't be needed in a system, however users would probably exist in such a system.

That last point is both rather opinion based and pretty thin reasoning, however it is the preference I use when modeling user/group relationships, additionally the API becomes much cleaner if I also need to handle groups of groups (all membership actions are handled on the group side).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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