5

I'm designing a RESTful API and have come across a problem when it comes to designing my routes, specifically the admin routes. My application currently has 2 types of users: regular users and administrators, and as of right now I have the following routes:

Users:

  • GET /users/current - gets the user object for the currently logged-in user that contains stuff like date of birth etc.
  • GET /users/{id} - admin route

Appointments (this is where I have ran into a problem):

  • GET /appointments - gets the appointments for the currently logged in user
  • GET /appointments/{id} - gets the appointment by ID, fails for regular users if the appointment doesn't belong to them with 403 Forbidden, succeeds for any id for admins
  • What should the route to get all appointments be for admins? GET /appointments/all?
  • similarly what about POST? POST /appointments creates an appointment for a currently logged in user, what about the admin route that just creates an appointment for a given user?

How would you tidy up this design? I've tried to look what the go-to way for this is but was unable to find guidance.

4

Authorization doesn't need to be part of the route. In fact it is better if it isn't.

GET /users/{id} is fully sufficient as a single route to get user data. Upon login, the client should know the ID of the user and should be able to query for the data of the current user by ID. If the client asks for data they lack authorization for, they'll get a 403 Forbidden. That is exactly what that status code is for. Similarly, an admin can just query for the IDs they are interested in. If you must, you can of course use current as magic value and have the server fill that in.

Similarly, I'd use GET /users/{id}/appointment to query for appointments by user ID. Get all appointments for that user, authorize if the client is allowed to see it. For normal users, they are only authorized to see their own, for admins they can also see others.

This leaves you free to create routes just as GET /appointments/ to query all appointments (which only be authorized for admins).

Similarly, a user would use POST /user/{id}/appointment/ to create an appointment, DELETE /user/{id}/appointment/{id} to delete it, and PUT /user/{id}/appointment/{id} to update it (or PATCH, if you support partial updates). An admin would use the very same routes to create, delete or update appointments for another user.

There is nothing wrong with introducing hierarchies to RESTful APIs if there is data with subordinate data. In this case, I assume that appointments are booked for users and that it doesn't make sense for appointments without a user to exist.

An alternative is not to use REST but to go with GraphQL, which allows for easier querying and updating of complex and data types with lots of relations.

6
  • Another option for GET /appointments/ would be to query all appointments the current user is allowed to see. That would be only their own appointments for a regular user and the appointments of all users for an administrator. Jan 12 at 8:03
  • @BartvanIngenSchenau I do not like query state being based upon context, especially not fort REST APIs. Imho it is best to have the query completely describe what data is queried, and not hide the context somewhere. Having a current magic ID is already a compromise, but at least its explicit in the route what happens.
    – Polygnome
    Jan 12 at 10:51
  • For me, an endpoint description of "all the appointments you are allowed to access" is in the same category as what you describe as responses for GET /users/{id}, where you can get a 403 response or a user object based on your authorization level. Jan 12 at 13:06
  • @BartvanIngenSchenau Sure, its a possibility. Its not entirely wrong to have some redundancy in endpoints, either. However, there is a conceptual difference. If GET /appointments/ delivers all appointments that the user has access to, then it is circumstance that as a normal user, you get exactly your appointments. This falls apart as soon as you have more complex authorization schemes where a user might be able to access more then just their own appointments. And it means its impossible for an admin to query only their appointments.
    – Polygnome
    Jan 12 at 14:55
  • @BartvanIngenSchenau By having a GET /user/{id}/appointment endpoint, you have a contractual guarantee to get exactly what you need and ask for and don't depend on circumstance to get the right answer.
    – Polygnome
    Jan 12 at 14:59

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.