March 31, 2010

Why 'Action Resources' are a REST Anti Pattern

REST's uniform interface constraint requires the operations that can be invoked on a resource to be generic, meaning that the applicability of an operation must not depend on the actual nature of the target resource. The uniform interface does not prohibit additional methods to be defined but requires any extension method to be generic. PATCH or MONITOR (slide 16) for example are valid extensions, while ORDER or PAY are not.

Our OO-biased brains are trained to think in terms of classes and associated operations (Cart.order()) and it apparently takes a considerable amount of time for our brains to re-wire and think in terms of transferring representations to modify resource state.

As a result, people are tempted to come up with ways to map non-uniform operations onto HTTP's uniform interface. One offspring of such endeavor is the REST anti pattern of Action Resources.

I have not tracked it back to its origin, but the general form goes something like this:

Given an operation foo(), define a link semantic 'foo' that enables the server to tell the client what the URI is of 'the foo-action resource of some other resource R. Knowing the foo-action resource Rfoo, the client would then be able to invoke the foo() operation on R by means of an empty POST to Rfoo:

Find foo-action resource of R:

HEAD /items/56

200 Ok
Link: ;rel=foo

Invoke foo() on R:

POST /items/56/foo
Content-Length: 0

204 No Content

So, why am I calling it an anti pattern?

Action resources are an anti pattern because the approach violates REST's self descriptive messages constraint. How so? Because the meaning of the POST request depends on resource state at the time the client learned that /items/56/foo is the foo-action resource of /items/56. There is nothing in the request that allows the server to understand the actual intention of the client at the time the server handles the POST request.

Suppose the client issues the above HEAD request at time T1, the server replies at T2 and the client receives the response at T3. By the time T4 the client sends the POST request to the action resource (and T5 when the server actually receives it) the server might have changed and is now looking at the POST with the empty body which translates to the client intention of telling the resource /items/56/foo to process this [empty body].

The server does not know that the request semantics depend on the server state at T2 when the server created the HEAD response and therefore cannot detect any mismatch between client intention and its own interpretation.