Richard Bucker

REST APIs, versions, and the stratification of error responses

Posted at — Mar 15, 2014

Over the last few years I have been constructing a number of REST-like services. Each time I refine my process and design principles; this time I’m going to address server side errors with a modest sidebar to REST API versions.I really like the Requests toolkit for python. The example on the home page makes it clear what we should all aspire to. Let me point out the use of the r.headers[‘content-type’]. A recent article I read suggested that the designed mechanism is putting the version in the path.http://example.com/api/V2/createI suppose this is functional but it causes a number of challenges. The first is that the infrastructure needs to be able to generate relative references and to be aware of the API version numbers and that it has to be across all APIs.  So it’s an all or nothing approach.The other approach, which I prefer but is is not very Requests friendly is changing the Accept and Content-Type in the header. Something like this:http://example.com/api/createwhere the Content-Type might be:application/myapp-create-request;v=2with a matching Accept:application/myapp-create-response;v=2 of course there might be a few variations on this but on the whole it provides for a better and cleaner routing and implementation process. On the whole the different versions can coexist in the same application space or be routed through a A/B reverse proxy component.So much for a brief sidebar on message versions.Unless your application is running naked you’re going to have some infrastructure running between your application and the client. Once the transaction leaves your DMZ you lose all control over everything from availability to recovery. So there are many more things to consider.For example; The basic response message contains a StatusCode in the response payload. The StatusCode; it’s definition, it’s values and interpretation is described in the RFC; can be interpreted to mean multiple things. In a normal HTML transaction a 200 means that the request was received, processed, and a response was sent to the client. 4XX usually indicates some sort of authentication or request error and 5XX usually indicates that there is an application error which is typically a crash or non-response of some kind.But then you have to ask yourself; what should the StatusCode value be when the application determines there is an error? Like when a parameter is missing or might have a wrong value or format. How do you indicate that there was an error and not mess with the StatusCode? Recently I refactored all of my error handlers so that they returned a 400 instead of a 200… along with an error payload.I think if I had defined the transactions in a more formal manner I would have some to this conclusion a lot sooner. (a) Leave the StatusCode to the infrastructure (b) any non-200 means that the infrastructure is experiencing some pain. (c) when generating responses use the Accept header to determine the format of the response (JSON, XML, plain text, msgpack, …) and use the Content-Type to specify the return type:application/json;fmt=create;v=2Now… between the path and Content-Type the router and the controller know exactly what to do with this request and the client knows exactly what to do with the response.  In fact the interface could be completely decoupled from the workflow.