I'm struggling with defining proper endpoints for a RESTful API, given the following requirements:
We have an existing /customers
endpoint, which when called with the GET verb, returns a list of customers, which look like so:
{
"id": "some-id",
"name": "John doe",
"email": "[email protected]",
"address": "Somestreet 1, 1234 Somecity, Somecountry",
"phone": "+1 234 567 765"
}
I now have a requirement to introduce a new user type to our system, these users are allowed to query the /customers
endpoint, but are not allowed to see the same data about the customers:
- emails should be masked (e.g.
j***.d**@example.com
) - address should only contain city-level information (e.g.
Somecity, Somecountry
) - Phone numbers should not be visible at all
What is, in RESTful programming, the best way to deal with these requirements? I have thought about the following approaches:
Design a completely different resource
I could design another REST resource, which would live under another endpoint like /customers-basic
, and provide exactly the model that is intended for the new user type. This doesn't feel maintainable though, in an environment that is considerably complexer than the very simple example I provided, what if you have 3,4 different user types that all need to see different information about customers, I would quickly be challenged with coming up with names for all the different resource types and their routes.
Return different response types
While it's perfectly possible (using ASP.NET 6 at least) to return different models for the same resource, I think this would not be considered correct use of the REST principles. It would also make it hard to make a public specification (OpenAPI 3 e.g.) which describes which model is returned in which scenario
Making the sensitive properties sub-resources
Another solution would be to remove all sensitive properties from the customer
resource, and provide them as a sub-resource of the customer
(e.g. /customers/{customer-id}/email
). This would solve making certain properties completely unavailable to some users (returning a 403 if they requested this protected sub-resource), but it still wouldn't solve the masking of the email
property. Also this solutions would require additional API calls from the client application to completely visualize the resource. If you would need these properties in a paginated list view that would exponentially increase the number of API calls you would have to do to a point where it could potentially start getting detrimental to the applications performance.
Set unavailable properties to null
This would not solve the masking issue, but it would be a solution to properties you would want to completely deny showing to the user with limited permissions. Although in the case of optional properties (phone number e.g.) you would not be able to differentiate between not having a phone number, and the number being hidden from you.
GraphQL
Slightly off-topic, but if a different protocol does provide a solution, it should be mentioned here. I don't know a lot about GraphQL, but I do know it provides means of specifying which properties are available to a client, and then the client specifying which properties of a resource it wants to see. What I couldn't find is if it's common practice to limit a subset of properties to a higher-permission enabled user.
Any other approaches I'm missing? How do other people deal with these kind of requirements?
FYI: I'm working in a ASP.NET 6 environment, but it's really more of a design question than a technology question.