2

So let's say we have three simple resources: Groups, Users, and GroupUsers.

Groups - Represent interest groups which can be subscribed by users.
{
    name: 'Colorado Mountain Biking Group'
    ownerId: 1 (Some user)
}

GroupUsers - Represents the junction table in the many to many Groups - Users relationship. Group membership status and some other attributes are stored here. 
{
    userId: 2,
    courseId: 1,
    color: '#FFFFFF',
    nickname: 'MBG Colorado',
    status: 'accepted'
}

Since our API client will always handle the group from the perspective of the authenticated user, GET /api/groups/1 AUTH(userId=2) should return:

  Group (Includes GroupUser for authenticated user)
  {
      id: 1
      name: 'Colorado Mountain Biking Group'
      ownerId: 1,
      groupUser: {
          userId: 2,
          courseId: 1,
          color: '#FFFFFF',
          nickname: 'MBG Colorado',
          status: 'accepted'
      }
  }

Someone suggested to me that our API Clients should not know or care about the junction table, all they should care about is the group, so I should instead respond with a merged resource like so:

  group (In reality group merged with groupUsers for the authenticated user.)
 {
      id: 1
      name: 'Colorado Mountain Biking Group'
      ownerId: 1,
      userId: 2,
      color: '#FFFFFF',
      nickname: 'MBG Colorado',
      status: 'accepted'
 }

The problem I see with this (besides merging issues like repeated Id's and createdAt/updatedAt timestamps)is that the API clients will then use this same resource to further interact with our API endpoints.

If an API client wishes to update the groupUser resource he would:

PUT /api/groups/1 
{
      id: 1
      name: 'Colorado Mountain Biking Group'
      ownerId: 1,
      userId: 2,
      color: '#000000',
      nickname: 'Some other value',
      status: 'accepted'
 }

So now we would also need to scan request bodies and differentiate between group and groupUser attributes. Is the hassle worth it? Even when API consumers are other in-house developers?

  • Is it even a good idea to update both Groups and GroupUsers in the same PUT request? Can you think of a use case where the same action requires updating both the group's name and some user's status in that group? – Idan Arye May 23 '15 at 0:03
  • Group and GroupUsers values will be handled in the same frontend form. A user will go to the group settings page and be able to update all it's settings, if the user owns the group he will be able to update the name (visible to all group subscribers [Group]) and color (personal to the user [GroupUser]). – RedHusky May 25 '15 at 0:33
  • Would it not make more sense to have an endpoint like /users/2/groups that gives the user the groups they are belonging to? If you just have groups/1 the user then has to randomly go to groups to see if they are or aren't a member? – Cormac Mulhall May 25 '15 at 10:32
  • @CormacMulhall I was working on the assumption that we already know that group/1 belongs to user 2. Since authentication is done via a token, and these are user authenticated endpoints it seems redundant to have /api/users/2/groups since every request has to have the user token. In our case bearing a token with UserId = 2, replaces the /users/2 in the path. I should have mentioned the use of tokens in my post, sorry for the confusion. – RedHusky May 25 '15 at 22:12
  • I'm thinking more from the point of view of discovery by the client, rather than authentication. How does the client find out what groups he is a member of? Say you have a thousand groups 1-1000, but the user is only a member of 7 of them say, how does he get a list of the groups he is a member of without having to go to each one and see if a "groupUser" section exists in the JSON response? If you instead group by user then it is automatically implied that a group-user relationship exists, it does not need to be represented at all in the response. – Cormac Mulhall May 27 '15 at 14:30
1

You are facing a trade-off between two design principles:

  • first, simplicity/consistency: your original approach has the advantage that the names and structure correspond better to your server-side structures, what saves you some mapping effort. For example, imagine you introduce a color attribute into the Group resource later. So your first approach now will create a naming conflict, which has to be solved in the mapping.

  • second, API design should primarily done from the viewpoint of the client, whatever serves him best. Following this principle, the second approach is better (as long as it works and the additional effort is not unreasonable).

So you have to make a decision on what is more important to you and your coworkers, in your environment. The first approach may produce a little bit more effort for someone else, the second a little bit more effort for you.

  • Thank you, so it's all about being pragmatic then. I was worried that having these inconsistencies between the client and backend structures would have some other consequences that I might be overlooking. – RedHusky May 25 '15 at 0:37

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.