22 November 2014

The RESTafarian flame wars – common disagreements over REST API design

Cartoon originally found on Troy Hunt's blogThe phrase “RESTafarian” was coined by Mike Schinkel to describe over-zealous proponents of REST. It’s a description you can readily recognise if you have ever been caught in aggressively meticulous debates about what is and is not “RESTful“.

I have some sympathy with the zealots as REST does describe a particular architectural style whose subtleties are often poorly understood. That said, as with most architectural debates one always should usually prefer pragmatism over dogma.

The bottom line is that none of the main points of contention are significant enough to make the difference between success and failure. You will not look back on the smoking crater of a failed API development and wish you’d used POST rather than PUT, neither will you regard HATEOAS as the one thing that could have saved your project.

It’s important to retain a sense of proportion as being “right” isn’t always the same thing as being “successful”. Then again, if you’re going to call it “RESTful” then you might as well try to do it properly and consider the main points of contention that seem to keep RESTafarians awake at night.

If it doesn’t leverage hypertext, then it isn’t REST

The oft-quoted Richardson maturity model implies that an API can progress along a continuum from basic RPC calls over HTTP towards full REST.  The first step involves adopting resources, the second leverages HTTP verbs to manipulate resources and the final step of HATEOAS is required for the full “glory” of REST.

Maturity models can be unhelpful as they often describe a false continuum. People can gain the impression that there are different “styles” of REST or that HATEOAS is normally part of a more “mature” API. REST is a very specific architectural style rather than a menu of features. If you’re not using resources, methods and HATEOAS then it isn’t really a RESTful API.

Roy Fielding wrote the initial thesis that defined REST and is pretty explicit about this:

“If the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API.”

He does go on to hammer the point home in greater detail:

“A REST API should be entered with no prior knowledge beyond the initial URI… From that point on, all application state transitions must be driven by client selection of server-provided choice… “

It can be difficult to realise the benefits of HATEOAS as you are dependent on savvy consumers who understand the intention of your design. You can’t stop them from hacking URLs directly rather than using the resource links provided to them by the API. You can take a horse to water, but you can’t make it navigate your resources correctly.

Some API designers decide that HATEOAS is more trouble than it’s worth. That’s perfectly valid and many excellent APIs do not adopt HATEOAS. It’s just that they’re not really “RESTful” in a strict sense.

PUT vs POST

Which should you use for creating new resources? The correct answer, inevitably, is “it sort of depends“.

If you know the location of the resource then use PUT, otherwise POST is probably more appropriate. It’s also important to bear in mind that PUT should be idempotent, i.e. repeating the operation yields the same result every time.

Therefore, you could use either to create new resources. Adding a new resource without knowing what the new URI will be is a POST operation as each repeated call will yield a new resource. If the URI is known then PUT is more appropriate because successive calls will not create a new resource.

Doing PATCH “properly”

According to the HTTP specification, PUT must take the full resource representation in the request. This can be a bit cumbersome, so PATCH is increasingly being adopted for partial updates.

Given that PATCH is only a proposed standard there are details around the semantics of the method that are not widely understood. It’s not a simple replacement for POST and PUT where you supply a flat list of values to change. The request should supply a set of instructions for updating an entity and these should be applied atomically.

A “set of instructions” is very different from a “set of values” and the specification clearly states that a request should be a different content type to the resource being modified. The detail of the representation is down to you, but RFC6902 describes a JSON format for PATCH where each object represents a single operation, e.g. “add”, “copy”, “delete” and so on. Each type of operation is described in eye-watering detail in the specification.

The point here is that there is a lot more to PATCH than meets the eye. Unless you adopt a complex semantic format for describing change then you are likely to arouse the ire of the “you’re doing it wrong” brigade.

Authentication

REST doesn’t say anything about authentication, so there’s no “correct” way of doing it.

With any authentication mechanism it’s important to remember the “stateless” aspect of REST. All the information necessary for the call should be contained within the request itself. You should not be tempted to do anything that adds state into the equation, such as login mechanisms that set cookies.

You should use fixed addresses for resources

This is the HATEOAS argument in a different skin. Roy Fielding is pretty clear about this:

“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.”

This is rarely understood by developers who often can expend vast amounts of energy considering the “correct” format for URLs. Your integrators shouldn’t care as they are just following links provided by your resources. Well, they do if it’s “RESTful”.

REST and CRUD

REST and CRUD are not the same thing.

CRUD describes a series of common operations that can be performed on entities in a data repository. REST is an architectural style of API where operations are performed on abstract representations of resources.

That’s all there is to say, really.

Is REST part of HTTP?

Once again, we can count on Roy:

“A REST API should not be dependent on any single communication protocol”

Strictly speaking, REST is an architectural style is not necessarily bound to any protocols such as HTTP. However, in practice you will never be likely to use anything else.

Versioning

Software evolves, so you inevitably need some kind of strategy for versioning an API. REST doesn’t provide for any specific versioning but the more commonly used approaches fall into three camps: putting it on the URI, using a custom request header or a adding it to the HTTP Accept header.

All of these approaches have their faults and can be undermined quite easily. URIs should refer to a unique resource rather than versions of a resource. A custom header seems like an unnecessary repetition of the Accept header specified by HTTP, while using the Accept header itself undermines the notion of being able to identify the resource through the URI.

Clearly, you can tie yourself in knots with versioning and REST. Some people even advocate avoiding the issue altogether and adopting techniques such as tolerant readers to allow for loosely defined and flexible contracts. That involves opening a wholly different can of worms.

Finally… remember what actually matters…

The very most important thing is that you have an API that your consumers find consistent and usable. This is not necessarily the same thing as being “RESTful”. I have seen many projects flounder as architectural purity takes precedence over usability.

Many design decisions should be dictated by a clear-eyed view of what your consumers expect from an API. Will consumers benefit from an API driven by hypertext or do they just want to hack a few simple URLs? There’s nothing wrong with a good old HTTP API that borrows a few aspects of REST. Just don’t call it a “RESTful” API…

Filed under API design, Favourite posts, Rants, REST, Web services.