Swagger/Apiary/RAML versus REST
There is a style of API that has become very popular that is often documented with tools like Swagger, Apiary or RAML. This style of API is often colloquially called REST, although it is not a hypertext style and so does not conform to the formal definition of REST. I'll call it 'pseudo-REST'. This note uses a worked example to show how this style is different from a 'hypertext-REST' API, what the 'hypertext-REST' equivalent of the API would be, and what the advantages of the hypertext-REST style are.
Summary
I believe the 'hypertext-REST' API is simpler and easier to use for the client and achieves better decoupling of the client and the server. I think that any server implementer or designer who wishes to offer clients the best possible API should consider using the 'hypertext-REST' style, rather than the 'pseudo-REST' style. API documentation tools should be enhanced to support the 'hypertext-REST' style. The 'hypertext-REST' API style is slightly harder to implement on the server, because the server has to take responsibility for translating between URLs and database keys. In the 'pseudo-REST' style, this responsibility is pushed to the client, so simplicity for the server is traded for simplicity for the client.
The example
Assume an application that stores information about dogs and owners. Each dog has a name, breed, date-of-birth, and a reference to its owner. Each owner has a name and address.
The implementation
Assume that the implementation exposes URLs like the following:
/dogs
/dogs/{dog-id}
/people
/people/{person-id}
Clients of the API can POST new dogs to /dogs, and new owners to /people. Clients can GET, PUT (or PATCH) and DELETE using the URLs of individual dogs and people. You are all very familiar with this pattern.
Both the pseudo-REST version of my example and the hypertext-REST version of my example can use the same URLs in the implementation, and support the same operations on those URLs. I think this similarity is part of the reason that so many people confuse pseudo-REST with hypertext-REST. However, there is a significant difference in the information that the client has to know when using the two styles.
The pseudo-REST API
In the pseudo-REST example, the representation of a dog might look like this in JSON:
{
"name": "Lassie",
"breed": "Collie",
"date-of-birth": "1940-01-01",
"owner": "123456"
}
In the pseudo-REST style, the link to the owner is represented by an opaque identifier - 123456. In order to use this ID, the client of the API needs a second piece of information - the URL template for people, i.e.
/people/{person-id}
The client will also need other information, like the domain name of the server, and any base path segments that need to be prepended to the URL template. Swagger and similar tools provide languages for documenting all this information for clients. Using this information, a client can manufacture the following URL:
http://example.org/people/123456
The 'hypertext-REST' alternative
In a 'hypertext-REST' API, the server would implement the same URLs, but the representation of a dog is slightly different:
{
"name": "Lassie",
"breed": "Collie",
"date-of-birth": "1940-01-01",
"owner": "http://example.org/people/123456"
}
In the 'hypertext-REST' version of the example, the link from the dog to its owner is represented by an opaque HTTP URL. This illustrates the 'hypertext' characteristic of the 'hypertext-REST' model.
In this style of API, URLs are opaque to the client – the client does not manufacture a URL according a URL template, so the client does not need to learn URL templates and the API documentation does not have to describe them. This provides a significant simplification for the client. It also helps decouple the client from the server, allowing the server to change the URL structure in the future (it has to continue to support URLs it has already given out, of course, since they may have been bookmarked).
Documenting the two styles
There is good tool support for documenting the pseudo-rest style. Here is an Apiary description of my example:
Using these tools to document a 'hypertext-REST' API doesn't appear to be easy, because they all seem to assume a URL is described in a template based on a path. In the 'hypertext-REST' model the URL structure is server implementation detail subject to change that does not appear in the API description. Here is an approximation using Apiary.
This is almost what I wanted to say, except for the '/' character before each {xxx-url} entry. The '/' would imply that the {xxx-url} value should be post-pended to http://example.org/ to form an URL. This is not the intention - {xxx-url} is already a complete URL. Unfortunately, Apiary appears not to accept an entry that does not have the leading '/'. Note that the example GET that Apiary generated is also not what I wanted. What I wanted was this:
GET <path and query string from {dog-url}> HTTP/1.1
Host: <host from {dog-url}>
Accept: application/json
It is possible that Apiary can do what I want and I just don't know how - let me know if you know something I don't.
Other differences
In the 'hypertext-REST' style, a URL provides both the identity and the location of a resource. In the pseudo-REST style, the identity and the location are separate, and the API client manufactures a location URL from the identity. Because of this, in a pseudo-REST style, it makes sense to define URLs that look like this:
http://example.org/V2/dogs/123456/json
http://example.org/V2/dogs/123456/xml
http://example.org/V1/dogs/123456/json
The intention of the first of these URLs is to provide access to a V2 JSON representation of the Dog that is identified by the id value 123456. The other URLs are analogous. This approach is reasonable in the pseudo-REST model, because in that model URLs only represent location – identity is represented by the value 123456.
While the above URLs are reasonable in the pseudo-REST model, they are not reasonable if you are trying to follow the hypertext-REST model. The reason is that the URLs above would each define a new and different resource, which is not the desired outcome[1]. In the 'hypertext-REST' model, you would do this instead:
GET /dogs/123456Host: example.org
Accept: application/json
Dog-Version: V2
In other words, you would put the 'V2' and 'JSON' information in headers, so that the URL itself – which indicates identity – is unaltered.
[1] Even in the hypertext-REST model, it is possible to define these additional URLs, but they cannot be used to link between resources – a 'canonical' URL like http://example.org/dogs/123456 has to be used for linking. The OpenStack API is an example of this model in use. Since in the 'hypertext-REST' model you don't want the client to manipulate URLs, you are also motivated to link all these resources together so the client can find them. In total, this results in a more complex model for the client and the server.