Reputation: 36319
I'm kinda surprised I don't see this in more REST discussions, but I'm debating how to best provide a list of things to a user, based on who the user is, or what they have authorization to see.
For example, let's say that we have an API for a bookstore. I might have a resource URL of /books
which would list all books. But if my application logic is such that some books are viewable to users who are anonymous, but others are only viewable to those who are logged in, I'm a bit more uncertain the best way to go.
Obviously, programmatically I could filter based on the identity of the user (gleaned from their API key or whatever creds I'm using) but I feel like that's a bit 'off' from the standpoint of REST design.
Feels more right to have two endpoints; let's say one for /books/public
and one for /books/restricted
and we can say that the latter returns a 401 if the user isn't logged in. Easy enough.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books
in that case, but that (again) feels off since the /my/books
isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235
or /users/1234/books
to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I guess that's a lot of background to my main question: What's the best practice for REST API URL design when the resource data is dependent on user identity?
Upvotes: 1
Views: 910
Reputation: 26129
Obviously, programmatically I could filter based on the identity of the user (gleaned from their API key or whatever creds I'm using) but I feel like that's a bit 'off' from the standpoint of REST design.
No, it is not off, because the credentials are part of every request and the response can be dependent on request parameters...
The url depends on whether you want to share personal data with others:
if you want to share the url
/users/{userId}/favouriteBooks/
/books/favouriteOf:{userId}/
if you don't want to share the url
/books/favourite/
Keep in mind that you always can change the url structure as long as your service applies the HATEOAS principle...
Upvotes: 1
Reputation: 11285
Rest is not a standard so there is technically no right or wrong way to accomplish what you're after. Having said that, I'll give you my opinion.
I propose you simply use /books
unless there is a very specific need for a service or user to see which are unique to them and those that are public. In which case I suggest doing all three. The fact that there could be more or less content at the /books
endpoint based on the user's privileges doesn't alter the intent, it enhances it.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books in that case, but that (again) feels off since the /my/books isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
I believe providing a /my/books
endpoint is ideal if the content is intended to be private. If your reading list IS private and specific to your account then having an endpoint without that is masked significantly dramatically reduces the risk of having user-identifying information leak via the url.
In the event that reading lists can be shared then you would want there to be a a discrete action. If a user wishes to share their reading list or a subset therein they would compose the list and a new endpoint would be created specifically for it like /book-lists/xxxx-xxxx-xxxx
. If your user's reading list is public by nature then /my/books
would simply redirect to /booklists/xxxx-xxxx-xxxx
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235 or /users/1234/books to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I'm not sure if you are referring to the reading list or the first concept of filtered results by some form of credentials.
/books/users/1234
would suggest a correlation to users from the book's perspective. This would be more applicable for authors or publishers, people that are actually affiliated to books in some deeper connection other than your examples above elude to. The same logical connection, for me at least, applies to /users/3493/books
but to a lesser extent.
I think as a general approach, you should scratch both of these out if you have any desire for the public links to be shared. You are potentially making a very convoluted structure that could ultimately result in people getting really confused why their friend can't see a list of novels because she sent her search results with a url had been "personalized" and thus secured.
Upvotes: 2