16

Let's say I have an app to manage sporting teams. Potential users have a role of 'coach', 'player', and 'fan'. In many cases they will have to make similar api calls to retrieve info like this:

/api/v1/players    (retrieve all players on their current team)
/api/v1/events     (retrieve all events for their current team)

In other cases roles might have certain features only available to them, like this:

/api/v1/opposing_players/:opposing_player_id  (retrieve scouting info about an opposing player, not available to fans)

My question boils down to... Should I explicitly break out the apis with a prefix for each role? Or should the API only have one base url?

/api/v1/coaches/events
/api/v1/fans/events
/api/v1/players/events

or

/api/v1/events
5
  • Are those events different for each type? Is there any related security issue, like giving access only to certain parts of your API?
    – scriptin
    Jul 28, 2017 at 16:11
  • For the sake of this question let's assume that 'events' in the last example is the same, but the roles might dictate which events are returned. For your second question, I'd like to hear from people who have had to make this trade-off as part of the answer. Is the overhead of maintaining three separate apis worth being able to move authorization/security upstream? Or should I just bite the bullet and ensure the single api endpoint behaves well and securely for all role types at the request level?
    – Msencenb
    Jul 28, 2017 at 16:16
  • Does your routing framework let you have routes like /api/v1/{role:string}/events where role is a parameter to the controller function? I have done lots of stuff where parts of the URL are actually parameters that get used in what ever function i am running. Jul 28, 2017 at 16:22
  • 1
    @WindRaven I want a higher level perspective. Let's ignore what I can do, as the framework can be made to do anything. How does the tradeoff of separate apis vs a single api that uses role based authorization under the hood play out initially and at scale? Load balancing, technical complexity, maintenance overhead, etc.
    – Msencenb
    Jul 28, 2017 at 16:43
  • Look at It from the consumer point of view. Which one is easier to consume? Does the consumer need to know the relationship role-event? Why? In which way it represents better your API than the single endpoint? The answer depends on how you want the API to be consumed.
    – Laiv
    Jul 28, 2017 at 17:56

1 Answer 1

26

No you shouldn't use level of access as part of the URI.

There is already a standard way to separate API by user access, and that's with authorization. All users should access the same endpoints, and based on the authenticated user's role, or attributes, you can

  • Return 401 Unauthorized - If the user is not authenticated
  • Return 403 Forbidden - If user doesn't have the appropriate role or attributes to access a resource
  • Return different models based on level of access. E.g. coach may be allowed to see each player's birthdate but fans are not allowed. you can show/hide attributes of models based on access.

If you did use a separate API scheme for each role, here are some drawbacks:

  • How do you represent API that are the same for all roles?
  • Must duplicate a lot of routes when a new role is added (e.g. system admin role, or division manager)
  • Changing the name of a role will break the API?
  • Unintuitive - if I'm a coach but I want to access player data, I might think I should access /api/v1/players but really I need to access /api/v1/coaches/players or something
  • How do you represent clients that are players and fans? I.e. multiple roles
  • How do you support allowing unauthenticated clients? I.e. no roles
  • Redundant data - you have to check each user is authorized to access a resource, but now you must also check that the user's role matches the URI . You have to handle the edge case of when the user's role doesn't match the URI role.
  • Changing authorization is an interface-breaking change. Say a coach retires and becomes a fan. They must now change all the endpoints that they used to access.
2
  • Hmm how about API per feature? A great example is the "UpdateSelf" vs "UpdateUser". In the past I used to "get all users that I can update" and then work from there, but it might get very complex. Now I just decided that UpdateSelf is part of a "SessionService" and "UpdateUser" is some admin service. So it depends how you want to name the split, are you splitting because it's a different feature or because it's an additional permission (different role).
    – Worthy7
    Nov 17, 2022 at 6:22
  • Although it's a few years on, I wanted to pop in and say that this answer has stood the test of time in my codebase. Putting the level of access into authorization layer (in my case via Pundit in a Rails app) has been great. The authorization layer has the added benefit of being a lot easier to unit test, as I don't need to hit a full blown API endpoint to understand if it's returning the right data. Anyways, thanks @Samuel for a great answer!
    – Msencenb
    Dec 19, 2022 at 21:51

Your Answer

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

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