4

I'm trying to figure out what's the correct RESTful way to structure an URL for user interactions with other entities?

For example, I have three entities: users, posts and tags.

How do you represent "user likes post", "user follows tag" or "user follows user".

This is what I had in mind, but I'm not sure if it's the correct way to do it:

POST /user/:id/like/post/:id (user likes post)

POST /user/:id/follow/user/:id (user follows another user)

Looks very semantic, but I'm not sure about the POST method

Jorge Pedret
  • 1,037
  • 1
  • 9
  • 16

2 Answers2

5

Fair warning: I am a newbie to REST as well.

Some general remarks, taken from RESTful Web Services by Leonard Richardson and Sam Ruby:

  • use plural for your resource names
  • use POST to the resource name if the server controls the identity of the resource
  • use PUT to the resource/:id if the client controls the identity of the resource

Applying that to your situation I can see the following resources

  • /users : list of users
  • /users/{id} : specific user
  • /users/{id}/tags : list of tags used by this specific user
  • /users/{id}/posts : list of posts liked by this specific user
  • /users/{id}/follows : list of users that this specific user is following

To add a post to the ones liked by a user, I would POST to /users/{id}/posts with the identification of the post(s) that are to be added to the list of liked posts in the request body.

Similary to record that user 1 is now also following user 200 and 300, I'd POST to /users/{id}/follows with the identifications of those users in the request body.

After all you are not supplying the identification of the relationship between user 1 and the other two users, but you are adding two new relationship resources that have user 200 and user 300 as their "follows user" attribute respectively.

A database backing your app would most likely have a Users table, a Posts table and a Likes where the Likes table is the one being added to when you record that a User likes a Post. And the identification of the Likes record will not be the User's identification nor the Post's identification, but it will have its own identification (separate or as a combination of the User's and Post's identifications).

Even if you were to store the Posts liked by a User in something other than a relational model, possibly even without separate identifications for the Likes', the identification of each Likes would still be the combination of the User and the Post.

Marjan Venema
  • 19,136
  • 6
  • 65
  • 79
  • Good explanation. Thanks. Old post I know but I'm thinking /users/{id}/posts would need to be reserved for getting all posts that belong to that user so an additional route of /users/{id}/likes would be needed wouldn't it? – isimmons Jul 07 '14 at 23:25
  • @isimmons: yes if you use `/users/{id}/posts` for all posts _by_ a user, then `/users/{id}/likes` is an option for the posts (possibly by others?) that a users has favorited/starred/whatever. Some would argue that query arguments could be used. I think that the semantics of the returned resource dictate that/when a different URI is needed. Posts authored by a user are something completely different from posts authored by whomever and marked as likes by a user. – Marjan Venema Jul 08 '14 at 07:24
1

RESTful interfaces have a single well known URL as a starting point. From there every other action and entity should be accessed by hypermedia controls provided in the responses. In that sense, the URL structure should not matter at all and should not matter to the client. They could be completely opaque (e.g., like http://bit.ly/9xhUgg) and the clients will still be able to work.

Here is some useful reading:

Having said that, from a server implementation point of view, it will easier using a structure like the one you have suggested. Something like:

GET /

<myapplication href="/">
    <users href="/users"/>
    <posts href="/posts"/>
    <tags href="/tags"/>
</myapplication>

GET /users

<users href="/users">
    <user username="johndoe" href="/users/1234"/>
    <user username="janedoe" href="/users/1235"/>
    <next href="/users?page=2"/>
    <search href="/users">
        <name type="string" cardinality="required"/>
    </search>
</users>

GET /users?page=2

<users href="/users">
    <user username="bobgeldof" href="/users/1236"/>
    <user username="biancajager" href="/users/1237"/>
    <next href="/users?page=3"/>
    <prev href="/users"/>
    <search href="/users">
        <name type="string" cardinality="required"/>
    </search>
</users>

GET /users/1236

<user username="bobgeldof" href="/users/1236">
    <posts-liked href="/users/1236/posts"/>
    <tags-followed href="/users/1236/tags"/>
    <users-followed href="/users/1236/users"/>
    <like-post href="/users/1236" method="PUT">
        <post type="URL" cardinality="required"/>
    </like-post>
    <follow-tag href="/users/1236" method="PUT">
        <tag type="URL" cardinality="required"/>
    </follow-tag>
    <follow-user href="/users/1236" method="PUT">
        <user type="URL" cardinality="required"/>
    </follow-user>
</user>

The important thing to not in the above is that when a post is being liked, or a tag or user is being followed, the URI for the post, tag or user is what is submitted as a parameter, not an ID. BTW the reason I use PUT for those operations is that the action is idempotent. If it wasn't I would use POST instead.

GET /users/1236/posts

<liked-posts href="/users/1236/posts">
    <post href="http://stackoverflow.com/questions/11566436" title="What's the RESTful way to structure an URL for user interactions with other entities?">
        <unlike href="/users/1236/posts?href=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F11566436" method="DELETE"/>
    </post>
    ...
    <next href="/users/1236/posts?page=2"/>
</liked-posts>

GET /users/1236/users

<followed-users href="/users/1236/users">
    <user username="biancajager" href="/users/1237">
        <unfollow href="/users/1236/users?href=%2Fusers%2F1237" method="DELETE">
    </user>
    <user username="Jorge Pedret" href="http://stackoverflow.com/users/201092/jorge-pedret">
        <unfollow href="/users/1236/users?href=http%3A%2F%2Fstackoverflow.com%2Fusers%2F201092%2Fjorge-pedret" method="DELETE"/>
    </user>
    <next href="/users/1236/users?page=2"/>
</followed-users>

The thing I really like about the above (besides it being truly RESTful) is that because you are using URLs instead of IDs, you can like and follow posts and users in other systems. :)

Tom Howard
  • 6,516
  • 35
  • 58