views:

526

answers:

8

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

A: 

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:

movies/10/edit
movies/10/ratings/add
movies/10/ratings/10/reply
movies/10/ratings/10/edit
movies/10/tags
movies/10/tags/add
movies/10/tags/10/remove

... 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:

movies/10/edit

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.

  • XML documents balloon the size of the actual data with a human-readable syntax when, in most cases XML is used, humans never actually read or edit the content themselves.
  • XML takes time and memory to load, parse and coerce the values inside attributes and nodes into a format that a computer can understand.

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:

<?php
    $this->SetConfig(array(
        "Database" => array(
            "host" => "localhost")));
?>

(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.

nlaq
REST is about seperating verbs from nouns really. The reason why it's mostly limited too GET, PUT, POST, DELETE is thats about the only verbs most webservers can cope with. IIS6 has problems with PUT out of the box for instance.
Martijn Laarman
One of the main ideas of REST is to use HTTP as an application layer protocol. Since HTTP already defines some semantic it is wasteful if it's only used as a data-layer protocol (i.e. all semantic is in the data transported via HTTP).
Joachim Sauer
What Martijn said: without the range of verbs appropriately used it isn't RESTful
annakata
Wait, now you have a problem with XML and you're writing off REST as foolish? Comments aren't good enough to deal with this, but you're off base to say the least. Start here: http://en.wikipedia.org/wiki/Representational_State_Transfer
annakata
Actually HTTP has alot of VERBS (more then 4). Url's describe resources (the r in url) they should not describe the action on that resource, the HTTP method ON that resource should. It's all about the sementics :)
Martijn Laarman
To add a note: this is how Tim Berners-Lee envisioned HTTP, it's not a fad we've been doing the web all wrong ever since it was created!
Martijn Laarman
The fundamental tenet of REST is resources, and actions upon those resources. The HTTP verbs are handy, but hardly required (HTTP is not necessary for a REST arch.). The URLs in REST are simply resource identifiers. The actions are separate (in HTTPs case, they're in the header.)
Will Hartung
XML being human readable is a huge benefit when trying to debug or diagnose issues; much less painful than debugging binary RPC. As for being a fad, it's a subset of SGML which is over 50 years old, and used throughout the web in RSS and even this site which is XHTML (i.e. XML) so I don't think so!
Greg Beech
XML is far from ideal but it's a standard we can rely on being parseable by almost anyone. Speed issues are long gone with decent soft AND hardware parsers these days. The bulkiness of XML is very compressable getting a compression rate of 50% or more is not uncommon. I dont get the hatred on XML.
Martijn Laarman
+1  A: 

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

<movie>
   <title>..</title>
   ..
   <rating url="movies/ratings/4">4</rating>
   <tags>
      <tag url="movies/tags/creative">creative</tag>
      ...

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

Martijn Laarman
Greg Beech
Question is however if you add a movie wouldnt you preffer posting 1 dataset one time over doing dozens of HTTP Requests ? In the above example tags are still a resource you can post too by the way ;).
Martijn Laarman
That's an interesting point actually. In my scenarios the movie object or equivalent will be read-only (only GET supported) so the tags couldn't be posted as part of it, but in if the object did support POST then I think that's a pretty good idea.
Greg Beech
Although... I could treat the collection of tags associated with the asset as a single resource, and do GET/POST/PUT/DELETE actions on the group as a whole. That seems cleaner than what I was planning... cheers!
Greg Beech
It seems delicious has gone the same route. http://www.peej.co.uk/articles/restfully-delicious.htmlbookmarks have tags but theres no /bookmarks/x/tags resource only a /tags/tag resource. Not saying this is definitive though i think neither implementation is wrong.
Martijn Laarman
Would it be reasonable to implement POST on /movie/n with the proviso that only <rating> and <tags> would be used? That would clear up the issue with the /movie/n/rating URI too.
Greg Beech
Must be my dutch tongue kicking in but do you mean no <title> <description> metadata in the POST ?
Martijn Laarman
Yeah that was what I meant; the POST would just have the user's rating and/or the updated tags... but it just feels wrong to me really...
Greg Beech
Well you can, most implementations i've seen allow you to drop properties you dont want to act on. for POST i can image <title><description><genre> being mandatory. But in a put you could leave them out of the body to leave them unchanged.
Martijn Laarman
A: 

Based on my understanding of ROA (I'm only on chapter five of RESTful Web Services) it looks good to me.

Neil D
+1  A: 

I disagree with the edit. Queries should be defined by querystrings as per Martijn Laarman's post. i.e.:

/movies?genre=action&timeframe=90s&lbound=20&ubound=40&order=desc
annakata
I don't agree with that - it murders SEO and makes URIs less human readable. Also it forces a dependency on what you name your querystring vars.
nlaq
Greg Beech
REST is about using the HTTP protocol to your benefit url variables were implemented to filter a resource thats there purpose. Beats hacking up your own filtering in the URI.
Martijn Laarman
Greg your right though that this is still very much up for debate and alot of different implementations have sprung from that debate. Url vars is mostly my own preference since i think sementically it just makes more sense.
Martijn Laarman
@Martijn - It does make more sense to let the framework do the parsing etc. than to do it myself... so I think I'm with you guys on this one. Query parameters it is.
Greg Beech
glad to hear it :) as Martijn again beat me to saying, query params were exactly intended for this
annakata
@Martijn, REST has nothing to do with HTTP.
Wahnfrieden
@Wahnfrieden - It has everything to do with HTTP. The whole concept is centred on utilising HTTP GET/POST/PUT/DELETE for their built-in semantic value.
annakata
+6  A: 

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.

/movies/horror/10/
/movies/horror/
/movies/

The following seems funny to me because flag isn't a resource, it's a property of the movie.

/movies/5/comments/8/flag -> Funny
/movies/5/comments/8/     -> Gives me all properties of comment including flag

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.

/movies/horror/simple
/movies/horror/expanded

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

p => Page
n => number of items to display
sortby => field to sort by
asc => sort ascending

So I could end up with a URL like

/movies/horror/default?p=12&n=50&sortby=name

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.

/movies/horror/
GET -> Get resources as a list
POST -> Create, Update

/movies/horror/10/
GET -> Get resource as item
POST -> Update

I hope this helps.

Pretty much what i've been saying in all the comments but better organized and well put. I dont agree on the view though, imo a resource should be something you can apply all 4 verbs too. The view is just there for the sake of GET ...
Martijn Laarman
[continued] .. whilst in practice it's just a filter which we both agree on should be in url variables :)
Martijn Laarman
This is not REST. It is just RPC. You cannot define URIs as part of a RESTful API.
Wahnfrieden
A: 

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.

Mark Cidade
A: 

@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.

Neil D
A: 

This is not REST.

A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC's functional coupling].

http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

Wahnfrieden
I didn't say anything about the representation or how links would be communicated to a client. So I really don't see how this applies.
Greg Beech
Just as long as you don't specify these URIs out-of-band. It looked like this was an API you were assembling, not just a nice human readable naming convention.
Wahnfrieden