Tuesday, April 17, 2007

RESTful URLs for non-CRUD operations

A common way of designing a REST system is to recognize the resources and associate a logical hierarchy of URLs to them and perform the traditional CRUD operations by using the well-known HTTP methods (GET, POST, PUT and DELETE).

However, many systems have operations which don't quite fit into the CRUD paradigm. Say, I have a repository of persons. And say I have an id associated with every person which allows me to have nice URLs to each person as such:

http://example.com/persons/1
This URL obviously GETs me the details pertaining to person 1. Now what if I wanted this person to talk and walk. I can think of 3 approaches to designing this scenario:
  1. Operations as resources: Which means I can have URLs such as:
    http://example.com/persons/1/talk
    http://example.com/persons/1/walk
    However, walk and talk are obviously not resources and designing it this way might be considered unRESTful(?).


  2. Operation as a query parameter: This leads to URLs such as:
    http://example.com/persons/1?operation=talk
    http://example.com/persons/1?operation=walk
    A drawback of this approach comes to the fore if you need other parameters to perform an operation. So, for instance, if you had to specify what to talk or where to walk. You'll end up with URLs such as these:
    http://example.com/persons/1?operation=talk&what=Hello
    http://example.com/persons/1?operation=walk&where=North
    As you can see, with this approach you end up having resources with overloaded operations and parameters. And you have to be aware of these combinations yourself and also explain them to your users.


  3. Matrix URIs: Specify the operations using matrix-like URIs. With this, your URLs look as such:
    http://example.com/persons/1;talk
    http://example.com/persons/1;walk
    And you can specify the operation parameters using the traditional query string:
    http://example.com/persons/1;talk?what=Hello
    http://example.com/persons/1;walk?where=North
    With this approach you have 3 distinct ways of representing 3 different things - slashes (/) for hierarchical resources, semi-colons(;) for operations and query strings (?param1=val1&param2=val2) for parameters.

Although I like the clarity of #3, I haven't seen this approach used all that much. Which makes me reluctant using it myself. Are there any web systems out there that use this approach? Are there any drawbacks to this approach which is why it is not widely employed? Are there any other approaches to designing URLs for operations?

Many questions. Any answers?

9 comments:

Stefan Tilkov said...

All three of them are not RESTful - because you misunderstand REST to mean CRUD. It doesn't.

The example you chose is possibly not the best one, but try this: If a person walks, a GET will return its status as "walking". If it talks, its status will be "talking". So the best match would be a PUT - change the status of the person to either "walking" or "talking".

I can see how this is probably not going to convince you ... so let's extend it a little: if a person is "talking", this may mean that he or she says something - let's call it "a quote". So maybe we add one entry to the person's history of quotes, which could reside at http://example.com/persons/1/quotes. So this could be a POST to .../quotes, containing the quote in the body, returning the URI of the new quote.

What can see is that REST requires some departure from what you are used to -- which is why the CRUD analogy is so misleading.

Keyur said...

Thanks Stefan. I understand where you are coming from. Sean Gillies also suggested something similar.

I come at it from a legacy perspective. If my users are used to people "talking", it's difficult for me to convince them that no people no longer "talk" but they leave "quotes".

I agree "quotes" is the RESTful way but as much as I don't like it, I might have to deviate from the pure REST principles in some cases and find a middle ground.

miguel.covas@rbcdexia-is.es said...

From my point of view it is always possible to design a subrdinate resource which describes a particular state of its master resource.

You can have

../persons/1/activity

If you GET that resource you can obtain, perhaps,
Activity: Walking
To: The Mall

And you could as well PUT a proper document
describing the activity and change from walking to running.

You could have ../activities and POST activities as in a queue. There is a lot of possiblities and you can always use the paradigm of accessing a resource or a list of resources

Stefan Tilkov said...

It's perfectly fine to not use the REST approach if you know what you lose (and if this is acceptable). You just need to be aware that your proposed solution is not on a middle ground -- operation names in URLs, especially state-changing ones, indicate that your design is unRESTful.

I'm really trying not to come across as a zealot (which is always a risk for a RESTafarian). I just want to point out that it's like suggesting to use OO with only static (class) methods and without inheritance because that makes it easier for procedural programmers.

Keyur said...

@miguel - interesting perspective... I'll muse over it.

@stefan - I like your OO analogy. And I think it's ok to be a zealot - I know I can be at times.

Ok - I think my example wasn't a good one. While the operations I had in mind were operations alright, they weren't state changing ones. I realize that my example definitely implies changing state. And I myself would endorse a PUT or at worst a POST in that case.

Sean Gillies said...

The similarity of our examples is not because Stefan and I lack imagination, but because there's a clearly right way to do it within the constraints. This is one of my favorite features of REST.

steven said...

or if you wanted the person to walk 5 miles to the north you might do something like
distanceNorth = 5 + old Y position

PUT person/1/position/y=distanceNorth

Anonymous said...

If you ask me, the RESTful approach to web services does not handle what you're talking about very cleanly. And people too often sacrifice clarity of the API in the name of conventional purity to the detriment of those using it.

Yes, you can torture the API to make it purely RESTful by modeling the concept of speech as adding a quote to a list of previous quotes, but does that really make for a good API?

And I do think that it is valid to attempt to find a middle ground between RESTful services and operational services (e.g. SOAP), and that the OP is doing just that. Just like putting static methods in an OO program can be seen as a middle ground between OO and procedure programming. C++ can be seen this way at a language level; it has both OO and procedural concepts.

I don't see what's so bad about having a library of REST services where some of the URLs aren't pure rest (e.g. person/123/talk) for the sake of API usability.

zayan said...

The below link contains the solution

http://stackoverflow.com/questions/16091947/how-to-route-non-crud-actions-in-a-restful-asp-net-web-api