REST Misconceptions Part 6 - Versioning and Hypermedia
This will be the last part about REST mistakes and confusion for the time being. In this post I will share my opinions about common approaches to versioning REST APIs. I will also present my view about why and how versioning could be avoided.
In this series:
- Misuse of URIs
- Not linked enough
- Leaky business
- Resources are application state
- REST “documentation”
How can I version my URI?
Troy Hunt wrote in his blog posting, that there are number of ways to version a REST API and all of them wrong. There are the 3 common ways, but together with a bunch of comments under Troy’s post, one could count up to 6 or maybe 8 various wrong options. First let’s look again at the most common and then I’ll try to convince you that there is in fact a way less wrong.
Version number in URI
Like most other problems with REST, a common flawed approach to API versioning stems from misunderstanding of the URI. This popular way simply adds a version number as a segment in the resource identifiers, such as in a template
This pattern could produce identifiers
as identifiers for the blue DDD book by Eric Evans. Immediately a question
should come to mind
Do these URLs identify two separate resources?
I think not. It’s the representation or the behaviour that changes and not the actual resource. In other words it is the contract that changes. The two identifiers are used to access the same resource. This is the most common argument why this is wrong. However this way is temptingly simple and will never break the cache constraint.
And of course there is one case when the version in URL makes sense. That is when the resource changes and it each individual revision must be accessible at the same time (see below). This however is not why entire APIs need versions.
Version in Accept header
Another option for serving multiple versions of an API is the use of Accept headers. The client would add the version
number to the request. In addition to (or instead of) plain
application/json, the server would allow custom media types
application/vnd.bookstore.v2+json to serve version 2 of a representation.
It has been noted that
for this solution to be symmetrical, the server should not only serve resources with various media types, but should also
accept requests with versioned payload. And there is even more edge cases when
DELETE method is concerned, which doesn’t
Also only part of the server contract could change. For example the server could return updated representation for version 2 but still expect the same request body as in version 1. Does it mean that both need a new version, even if only part of the API changes? I intuitively say yes, but I’m not so sure. Definitely dodgy space.
Another issue with
Accept header approach is that it will lead to media type proliferation.
Last of the common wrong ways is leveraging custom HTTP headers for requests:
The worst that can happen when applying API versioning this way is broken cache, where HTTP middleware ignores custom headers.
Also, both custom header and vendor media type have another caveat. What should the server return when the client doesn’t specify a version? Returning the latest will keep breaking clients while returning the oldest (supported) will likely stall adoption of the improved API.
What am I versioning?
Let’s take a step back and think about what actually can be version about an API. There are three possible answers:
- The resource itself (ie. the content)
- The resource representations
- The resource behaviour
Maintaining versions of a resource
As stated above, the first possibility is the only circumstance where adding a version number to a resource does indeed make sense. This could be used for a parallel-universe dbpedia, where individual revisions of a wikipedia resource are served as they changed over time. The main resource could be like
1 2 3 4 5 6 7 8 9 10
This means the each revision is a resource in it’s own rights and can be interacted with via representations.
Handling changes in resource representations
This is where resource custom media types sound like a viable options. A new version of the media type could be introduced to let the clients interact with the new and old representations. For example in the initial version of an API there can be some sort of person resource:
1 2 3 4 5
What if someone decided to change on
name field into
lastName. A breaking change at first glance,
but there is nothing wrong with including both the old and new property instead of replacing:
1 2 3 4 5 6 7 8
What about doing a
PUT operation on that resource you ask? The media type used should actually inform clients what are
the required inputs. So before the change there would be something similar to:
1 2 3 4 5 6 7 8 9 10 11
This tells clients that in order to modify a
Person resource, the
name property must be set. Now after the change
we would have a different situation:
1 2 3 4 5 6 7 8 9
This time round the description of the
Person class states that
lastName properties are required. The
name property is still there but marked as read-only and obsolete, which could mean that it will be removed in the future.
Thus if the server provides enough hypermedia descriptions and clients take advantage of them, no API versioning is actually required.
Evolving the resource behaviour
Last case is modifying how the client interacts with the resources. The comment linked above
talks about change in a blogging platform. In the first version the blog is simply posted and published immediately.
Such interaction could easily be modelled as a
POST to some blog resource.
1 2 3 4 5 6
Let’s say the the server returns a link to the newly created blog post. (I assume that the current date at the time of submitting is used in the URL).
So what happens when the contract changes so that a post is not published immediately but saved as draft? The first
request could stay the same, so there is nothing to break the client just yet. However the response would be different:
Location is returned. The client now gets the draft resource and learns that there is a link, which is used
1 2 3 4 5 6 7 8 9 10 11 12
The exact details of how the client should request are debatable and irrelevant really. All that matters is that the media type must be expressive enough to describe that interaction. And the client must follow.
I hope that my examples can convince some people out there that there are ways to avoid using version numbers within an API completely. Rich hypermedia used from day one should insulate the client from any breaking changes that the server may introduce.
You don’t version the API.
You don’t version the resource.
You don’t version your media type.
You version you hypermedia controls.
And of course, if you don’t have hypermedia controls you’re in trouble.
It seem as though almost any other argument for adding version numbers anywhere in a REST API is the consequence of not following the Hypermedia As The Engine Of Application State and thus introducing out-of-bound information on the client’s side of affairs.
Here are some more interesting reads: