When it comes to API versioning there are so many best practices and insights but there is still not a rock solid best practice.
In order to understand the Restful API versioning we first need to understand the problem.
The Versioning Problem
One shall not break your clients
Changing your API is a pretty fundamental thing to do. You are literally changing the interface that clients will have been communicating with prior to your change. The point being that you don't want to break the clients that connect to you today while still needing to alter (adding, removing or changing) your API. This doesn't affect just what the API looks like i.e. the request or response formats it could also include functionality e.g. defaults now work differently.
Unfortunately, no matter how brilliantly you architect your solution, you may well find that, over time, you have to change it.
It could be argued that micro-services get around this problem by being small. Micro-services should be so small they are so simple you won't have the space to change them - you can read more about what are micro-services, but I'm not going to talk about them today.
So, assuming that you do have to change your API then what you really want to do is to ensure that your clients know your API has changed and give them some way to programmatically decide, or otherwise, what version they are going to connect to so that they stay working.
Now that we understand the problem, what are the possible solutions?
The 4 Possible Solutions for API Versioning
Before I say any more I'd just like to state that each option has their particular pros and cons. Which one you end up choosing could be affected not only by best practice, restrictions of the infrastructure and other aspects. Currently there is no Best Solution for Restful API Versioning and there are even those who believe that we should not need to version our APIs at all like this post "Don’t Version Your Web API" by Jan Stenberg.
1) The URI
In this method the version is very explicitly put into the API URI. For example:
The options above show three different ways to expose versions through your URI;
Firstly, a coarse grained whole-of-hierarchy model and then a more finely grained sub-topic method giving us the ability to version separate elements of the API (roads in this case). The third option just shows the slightly less expressive model of having a single version argument (e.g. 'v2') without the explicit 'version' node in the URL hierarchy.
I, personally, do not like this model. From a purest standpoint it's argued that the URI in REST should represent the resource structure only.
A version is not a resource it is an attribute of the resource.
However, on a plus side I can see how it is very clear what's going on ! I also see this being recommended by many API tooling vendors. Here are some more pros and cons for this particular method.
- Ability to version specific resource branches. - Semantically dev friendly - Bookmarking is tightly coupled - Enables version navigation/discovery
- New versions change resource name and location - Complex proliferation of URI aliases - Bookmarking is tightly coupled (see pros!) - Enables version navigation/discovery (see pros!) - Can't use URI easily to compare identity - New versions break existing hyperlinks
Already we can see how advantages to one person could be seen as disadvantages to another.
URIs suck because they should represent the entity - I want to retrieve the maps entity, not a version of the breached account. Semantically, it’s not really correct but it is very easy to use!
2) The Accept Header
There is a well-known HTTP header called Accept which is sent on a request from a client to a server. For instance
This notation is saying that I, the client, would like the response to be in json please.
The accept headers is using this header to make up your own resource types, for example:
Accept: application/vnd.myapi.v2 + json
Now; hang on in here because that's a slightly strange syntax we've found ourselves looking at so let's walk through it....
The internet specifications say that I, as a vendor, can define (and have to register) a media type . If I do this it's called (no surprise) a 'vendor' media type. I should prefix my type with 'vnd' to make it clear it's my own definition. I then need to say what the actual type name is that I am defining e.g. 'myapi' . However, the specification states nothing about a version number so people have taken to saying that their media type name includes a version number, for example:
Now, because I, as the client application, still need to define what content type I really want (other than the version) this can be added as a suffix to the requested media type e.g. '+ json' in this case.
Please note that there is an alternative method of doing this without any pre-registering of the media-type using x. as the prefix:
The use of the Accept header feels like a very slight hack of the specification to me - but it works and is well-known, if not actually fully specified as such.
The biggest issue with this method is that it's rather hidden - and hidden things are always a little harder to work with. It's nearly always better to be explicit in what you are doing. I also suspect that asking your firewall admin to open up their firewall to any old vnd media type could lead to a quite big security risk.
However is looks much easier to pass around the Accept: vnd header than it is to pass around custom request headers.
Accept headers suck because they’re harder to test - I can no longer just give someone a URL and say "Here, follow this link", rather they have to carefully construct the request and configure the accept header appropriately.
3) Custom request header
In the original HTTP specs you could define any old HTTP header you liked in your client request as long as you pre-fixed with 'X-' e.g.
The specs changed and you can still pass any old request header through but it doesn't have to be pre-fixed with 'X-' anymore e.g.
this means you could ask the clients of your API to pass in something like
This option is not recommended nor used too much because of the following reasons:
Some of the routers (although today the things are different and most of them will pass on all the headers) can either reject the whole http request or just not pass the special header. Debugging this could be a very hard thing to do. I had the change to encounter such a thing where some machines were connecting through different routers and one particular router had been configured to remove non-standard header types. It allowed the message through - including all the other headers - but just silently removed the ones it didn't like!
It's hidden again - just like the Accept header. The Accept header is already a way to be quite explicit about what the client accepts so if we're going to hide stuff then at least let's use a way that's kind of known.
Custom request headers suck because it’s not really a semantic way of describing the resource - The HTTP spec gives us a means of requesting the nature we’d like the resource represented in by way of the accept header, why reproduce this?
4) The URI Parameter
Here's an obvious one that I don't see many people using:
This method is highly used by Amazon, Google and Netflix but still it is not so popular. Hmmm - maybe there's something to it?!
On the server-side, this feels a bit harder - the servers have to parse the entire param string before knowing where to route the request - something they try to avoid. Also, there is the same argument as not putting version numbers into the URI - the parameters are for specifying the services function not attributes of the implementation.
The client knows about what version he wants and the API provider has no problem to maintain several versions, when no version is specified the client will get the latest or default version. New versions will not break existing hyperlinks as we are using the url param and will not change the resource name and location.
Thinking versioning from Day One
By this I mean - when you design your actual API believe that it will change, even if not today - some day. This will affect what you code - not just your decisions as to where to put the version number.
If you're an agile developer then you could well be shouting at the screen right now.
Agile is all about today. Indeed, it specifically says - don't code for things that aren't in the spec. However, let's be clear: Coding for extensibility is not the same as actually putting in the extensions themselves - which is what the agile methodology talks about.
Leaving room for future versions fits into agile just fine.
Versioning an API is hard!
Yes it is, however, it's really no harder then keeping client and server code in synch has ever been. You should remember that Building Restful APIs is much easier that SOAP web services and is supported pretty much by all.
Try to allow for extensibility in your code from day one.
With regard to how to specify the version - If you do have a versioning convention in mind then make sure that your infrastructure can handle it.