Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST API design: one endpoint with if/else logic or two separate role based endpoints

I have an API design/versioning conundrum. Let's say I have an endpoint /api/customers which GETs all customers (ignore pagination). There's a twist though: if a regular user accesses this endpoint, they will only get the customers created by that user and no one else (I can check the access token and the sub field to determine who sent the request). Other usecase: if an admin accesses this endpoint, they should get ALL customers, regardless of who acquired them.

Now my question is from an API design perspective: is it better to have an if/else role check within the API controller itself to determine do I return ALL (admin) customers or specific (user) customers, OR should I differentiate between endpoints for the user and admin? I.e. admin only endpoint for all customers would be /api/admin/customers and regular users can still access their /api/customers?

like image 662
Ognjen Mišić Avatar asked Nov 05 '19 14:11

Ognjen Mišić


2 Answers

In REST, it is normal to have multiple resources that share the same representations.

For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static. These are two distinct resources, even if they both map to the same value at some point in time. The distinction is necessary so that both resources can be identified and referenced independently. A similar example from software engineering is the separate identification of a version-controlled source code file when referring to the "latest revision", "revision number 1.2.7", or "revision included with the Orange release." -- Fielding, 2000

It is perfectly consistent with that approach that you might have one resource for "all users", and another resource for "users created by Bob".

Where things get twisty is the case where you want to use the same resource identifier to provide different representations. That is, when Alice looks at "users created by me", she sees "users created by Alice", and when Bob looks at "users created by me", he sees "users created by Bob".

One possibility is to have "users created by me" redirect to the appropriate resource. It works, for values of "works" that permit extra round trips when the destination resource isn't already in the local cache.

In HTTP/2, server push may spare you some of that round trip pain.

The rules for shared caches should protect you from sending Alice's view of the "me" resource to Bob, and vice versa, but it is useful to be aware of the meanings of the various headers so that you don't inadvertently disable that protection.

Having different resources can be a problem in some "read your own writes" settings, because the caches won't know that an unsafe request has invalidated both resources. Bob creates a new user via a POST to "users created by me", and the corresponding cache entry is invalidated... but "all users" is a different cache key, and does not get invalidated. So if Bob looks at the all users view, he may see a previously cached copy without the changes that he just saw in his own view.

In some cases, it can make sense to consider sub-resources.

/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob

But if you are trying to reduce the amount of irrelevant data being exchanged, then that's not a good fit.

like image 61
VoiceOfUnreason Avatar answered Sep 23 '22 03:09

VoiceOfUnreason


It should be same endpoint. Otherwise, each front-end which calling your API must have the same logic to determine the role and endpoint mapping.

like image 24
Nelson Avatar answered Sep 21 '22 03:09

Nelson