Starting here Norm Walsh is blogging the development of his “Where in the World?” (WITW) web service. The laudable goal being to explore web services architecture and design issues by actually building something, and seeking feedback along the way. Excellent stuff!
Elsewhere David Megginson is prompting similar discussion by posing a number of REST design questions.
I love this kind of public design discussion, so I can’t help pitching to add my comments. Rather than leave them in Norm’s comment form I thought I’d write them up here, and use this new fangled trackback thing, like this: nwn-whereintheworld.
Taking a step back, we can see that WITW has two different kinds of resources: people and landmarks. Actually there may be three: people, locations and landmarks. But I’m not clear whether Norm is expecting to allow someone to store a series of “usual” locations with one of them being current.
So for now lets assume there are just the two resources. A person who has some attributes, including a location, and a landmark.
Following the spirit of a RESTful design both types of resources should have URIs. WITW provides URIs for people (here’s mine), but it doesn’t provide a URI for each landmark. This would be my first alternation to the interface. I’d probably add a
/landmarks “container” URI, and then
/landmarks/stonehenge, etc for each landmark as it gets added.
The reason for doing this is I can then manipulate a landmark independently to a given user. E.g. POST to update the information (add comments, associate photos, etc), GET to discover its location, who configured it, and who is nearby.
Let me elaborate on what I mean by “container”. There’s probably a more general RESTful term, but its terminology I’ve been using in my internal documentation.
A container is is simply a resource that contains others. So
/landmarks means “the set of all landmarks”. Typically a GET on this would return a list of landmarks or a query form; the URL may be parameterised to search for landmarks. I’d make
/witw/is be the container for people. Providing a hook for searching for users, listing all users, etc.
Generally I’ve designed things so that a POST to a container URI would allow me to create a member of that set (e.g. new landmark); i.e. modify that set. The response from that would be a 303 See Other, indicating the newly minted URI for the newly created landmark. This means that the server can retain control over the id allocation, but the client doesn’t need to do anything other than follow the links.
Using 303s to allow a server to assign an identifier for a newly created object is something I’ve commonly seen and used. I also return 303s after a POST to a given URI, to redirect the browser/client to the results.
I notice that WITW doesn’t do this which probably isn’t a big issue. However I think it would be better to POST to a the person resource, i.e.
/witw/is/ldodds to update my location rather than POST to
/witw/ami/now. At the moment I have to POST to that URL to change my location and it returns a document which is a subset of what my URI returns. Norm could remove one template and output generator from the application by simply returning a 303 to the users URI.
As a final comment I just spotted that the stylesheet that transforms the XML results into HTML for my browser annotates the document with more information than is available in the source XML, namely links to MapQuest. multimap, etc. It also spots the
userid attribute of
landmark and resolve it to a link to the relevent WITW user document.
This information should definitely be pushed down into the XML. It took me a while to realise that this was an important aspect to a RESTful design; “hypermedia as the engine of application state” is a pithy phrase but requires some unpacking. In essence the client application shouldn’t need to know how to generate additional URLs for your (or other) web services based on data in the response. The URLs should be provided, albeit in typed links/attributes, so that it can navigate to obtain additional behaviour. When designing a web site it’s a bad idea to present an end user with a “dead end”, this just applies the same criteria to machines.
It’s also got some neat side effects. Because your clients are then encouraged to just consume the URIs its provided rather than generate them, you can silently change your URL structure without having to co-ordinate with them; a GET is a GET.
Anyway, that’s all I could think of while munching my sandwiches this lunchtime. Looking forward to seeing WITW evolve further.