Paul
Paul

Reputation: 36319

REST API design for lists relevant to a user

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

Answers (2)

inf3rno
inf3rno

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

Chance
Chance

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

Related Questions