Saturday, 3 January 2009

GET and idempotence

I was reminded today of what seems to be a common misunderstanding of the idempotent requirements of a GET in a REST-ful architecture. GET doesn't mean the response must be the same each time; only that it must have no side effects, i.e. it should never cause the server's state to change.

For instance, CouchDB includes a resource, /_uuids, that returns a number of server-generated UUIDs. As far as I know, it only exists to support languages without a decent UUID library. It has no effect on the server.

However, CouchDB will only respond when /_uuids is POST'ed to:

$ curl -X GET http://localhost:5984/_uuids?count=2
$ curl -X POST http://localhost:5984/_uuids?count=2

One suggestion on the mailing list (although not from one of CouchDB's core developers) for the use of POST is to, "comply with REST as it returns a different output each time".

A GET would be just fine here. In fact, it would be more in keeping with the intended use of the HTTP methods.

It's easy to come up with examples of a REST-ful resource that sends a different response every request, with probably the most obvious being some sort of time server. Consider the following URLs:
  • /time
  • /time/BST
  • /time/EST
  • etc
You would GET the current time from an appropriate resource and I sure hope there will be a different response each time ;-). You could also PUT a time to one of those resource to set the time.


R Samuel Klatchko said...

While I don't know CouchDB internals, it sounds to me like the implementation of the unique ID does change the server state (and it would be too difficult to remove that from the implementation and still be able to provide a uniqueness guarantee) so the CouchDB implementers are doing the right thing.

The flaw in your example is that a time server is not required to require a unique response for every request. Two responses that are close enough together (or happening effectively simultaneously on different processors) can return the same response. Since it does not provide a uniqueness guarantee, a timeserver can be RESTful and use GET.

I doubt you can come up with an implementation of a UUID generator that is both stateless and portable.

Matt Goodall said...

Actually, the UUID generator changes no state in CouchDB. CouchDB relies on the uniqueness "guarantees" inherent in UUIDs to assume that the UUIDs are currently unused and do not need to be reserved.

It's safe to GET the resource multiple times, has no unintended side effects and does not affect other clients. Therefore, it seems to me that the _uuid resource, used in isolation, is both safe and idempotent.

Esaj said...

Hehe, it's the first time I've posted a sentence to a mailing list only to find it has sparked a whole blog post about the subject! Yes, the sentence on its own was an utter falsehood, but in my defence, I was trying to get at the fact that new unique UUIDs *must* be generated *every single time* they are requested, and any in-between proxies cannot cache anything or "optimise" multiple GET requests into one or anything like that.

Now, after further research into the HTTP RFC it seems that operations are all transparent unless otherwise explicitly requested, so in fact my worries were probably unfounded.

Matt Goodall said...

@Esaj sorry for singling out your specific post to the CouchDB mailing list but it really just reminded me of something I've seen in a few other places and always struck me as unnecessary.

For reference, here's the thread that started started this.

Thanks for the comments!

Esaj said...

@Matt That's perfectly fine, I am always happy to be corrected :-)