I'm doing research into a web API for my company, and it's starting to look like we might implement a RESTful one. I've read a couple of books about this now (O'Reilly's "RESTful web services" seeming the most useful) and have come up with the following set of URIs and operations for an object that can be commented on, tagged, and rated.
It doesn't really matter what the object is, as this scenario applies to many things on the net, but for the sake of argument lets say it's a movie.
Some of these seem to fit quite naturally, but others seem a bit forced (rating and tagging particularly) so does anybody have any suggestions about how these could be improved? I'll list them with the URI and then the supported verbs, and what I propose they would do.
/movies
GET = List movies
/movies/5
GET = Get movie 5
/movies/5/comments
GET = List comments on movie 5
POST = Create a new comment on movie 5
/movies/5/comments/8
GET = Get comment 8 on movie 5
POST = Reply to comment 8 on movie 5
PUT = Update comment 8 on movie 5
/movies/5/comments/8/flag
GET = Check whether the movies is flagged as inappropriate (404 if not)
PUT = Flag movie as inappropriate
/movies/5/rating
GET = Get the rating of the movie
POST = Add the user rating of the movie to the overall rating
Edit: My intention is that the movie object would contain its rating as a property, so I wouldn't really expect the GET method to be used here. The URI really exists so that the rating can be an individual resource that can be updated using the POST verb. I'm not sure if this is the best way of doing it, but I can't think of a better one
/movies/5/tags/tagname
GET = Check whether the movies is tagged with tagname (404 if not; but if it is tagged with the tag name should it return the actual tag resource by redirecting to something like /tags/tagname
?)
PUT = Add tag tagname to the movie, creating the tag resource /tags/tagname
if required
DELETE = Remove tag tagname from the movie, deleting the tag resource tags/tagname
if nothing is tagged with it after this removal
Note that these wouldn't be the entire URIs, for example the URI to list the movies would support filtering, paging and sorting. For this I was planning on something like:
/movies/action;90s/rating,desc/20-40
Where:
action;90s is a semi-colon delimited set of filter criteria
rating,desc is the sort order and direction
20-40 is the range of item indices to get
Any comments about this API scheme too?
Edit #1
This post is getting quite long now! After reading some of the answers and comments, this is the changes from above I'm planning on making:
Tags will be handled as a group rather than individually, so they will be at:
/movies/5/tags
GET = List tags
POST = Union of specified tags and existing tags
PUT = Replace any current tags with specified tags
DELETE = Delete all tags
I'm still really not sure how to handle flagging a comment though. One option is that instead of POSTing to a comment replying to it, a comment object will include its parent so it can be POSTed to the general URI, i.e.
/movie/5/comment
POST = Create a new comment (which may be a reply to a comment)
I could then use the POST to a comment to flag it. But this still doesn't feel quite right.
/movie/5/comment/8
POST = Flag comment
Most of what you have looks good. There were just a couple of strange things I saw. When I put my URLs together, I try to follow these four principles.
Peel the onion
If you make the R in REST really be a resource then the resource URL should be able to be peeled back and still be meaningful. If it doesn't make sense you should rethink how to organize the resource. So in the case below, each makes sense. I am either looking at a specific item, or a collection of items.
The following seems funny to me because
flag
isn't a resource, it's a property of themovie
.Define the View
The last peice of the URL describes how to show the resource. The URL
/movies/horror/
tells me I will have a collection of movies refined by horror. But there might be different ways I want to display that collection.The simple view might just be the title and an image. The expanded view would give a lot more information like description, synopsis, and ratings.
Helpers
After the resource has been limited and the proper view figured out, query string parameters are used to help the UI with the little stuff. The most common query string parameters I use are
So I could end up with a URL like
This will give me the list of
movies
limited to horror movies with the default view; starting on page 12 with 50 movies per page where the movies are sorted by name.Actions
The last thing needed are your action on the resource. The action are either collection based or item based.
I hope this helps.
Well, the way I see it some of the information you return now as objects could simply be added to the metadata of its parent object.
For instance, rating could be part of the response of /movies/5
Removing a tag simply means posting the above response without that tag.
Also queries should go in URL variables, I believe:
/movies/?startsWith=Forrest%20G&orderBy=DateAdded
Makes sense to me. Although I don't get why using the different HTTP methods is better then just using just the normal GET/POST and using a URL structure like:
... and so on. The latter makes much more sense to me and would be more clear IMO to the person consuming your web service. Also, the amount of operations you can perform on a "node" is undefined whereas using your current method you can only perform a set amount of methods (GET, PUT, POST, DELETE)
:)
If I am missing something (have not dealt with REST before) then please let me know why it is better to use the HTTP methods (thus limiting your operations and not feeling "right" with defining a difference between PUT and POST) over the conventional method.
In response to comments
Wait, so... HTTP defines four verbs; but also defines the ability for us to name our URIs whatever we want. So instead of appending the verb to the end of the URI:
We are just conforming to the verbs defined by the underlying protocol. That sounds foolish to me. I would need some hardcore convincing that this is a better way to do things in order to change my mind. It sounds a bit "fad"ish to me; like when AJAX came out and everybody jumped on using XML to represent data (thus slowing systems down because of the time it takes to parse and coerce) and consuming much more bandwidth then needed) and now we are stuck with SOAP.
How is limiting your verb vocab better?
I don't mean to sound hostile at all; but like I said, I don't like fads and this looks like one to me unless somebody can give me a reason not to think that way.
In response to comments #2
I have no problem with XML at all. As a Markup Language. As in eXtensible Markup Language. If you look at Wikipedia you will see that using XML as a data structure goes beyond its intended use. Don't get me wrong - if all things IT was always used in the ways that it was intended when it was built we would never evolve. But using XML over a binary format to transfer data from program to program is inane.
Using something like JSON (although that is not a binary format either - but it is a format which can be nativity executed on a JS VM) is much better for performance and memory allocation in AJAX - and as far as SOAP goes it simply blows my mind that a "computer-to-computer" protocol would be written in a language that was specifically designed to be human-editable.
Take Magento for example (the ecommerce platform). It uses one or more XML config file per module. But in order to make their already-very-slow application (I love Magento BTW, but it is MUCH MUCH slower then it needs to be) faster, they write code that serializes the array that was built using the XML config files so it can load faster. If they just did something like:
(this config file would be "required" from within the method of the config class that is instanced per module)
Instead of that XML crap the application would most certainly speed up; and would reduce the amount of "optimization code" they had to write.
Yes, XML is a fad. Yes, XML is over-used in situations it should never be used in. XML certainly has its place (XHTML, template engines) but NOT for storing config files, data files, or computer-to-computer interactions.
As far as REST goes, I misunderstood what it was. It does look interesting though; I'll look into it more though. But because of fads like XML I am hesitant to jump on these emerging practices.
This is an awesome initial draft for a spec of a REST API. The next step would to specify expected return codes (like you did with "404 No Tag Available"), acceptable Content-Types, and available content-types (e.g., HTML, JSON). Doing that should expose any additional chinks you'll need to hammer out.
This is not REST.
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
@Nelson LaQuet:
Using the HTTP methods as they are actually defined gives you the safety of knowing that executing a GET on anything on a web site or service won't eat your data or otherwise mangle it. As an example (pointed out in RESTful Web Services) Google's Web Accelerator expects this behaviour -- as stated in the FAQ -- and presumably other services do too.
Also it gets you idempotency for free. That is doing a GET, DELETE, HEAD or PUT on a resource more than once is the same as doing it only once. Thus if your request fails then all you have to do is run it again.