Recently, I have been thinking about REST API versioning. I have read several books and articles but the issue is far from straightforward.
Let’s start with a simple example – a petclinic. We will have two resources, pets and owners.
/pets/123 % supports GET, PUT, DELETE
{
"pet":{
"name":"Pluto",
"owner":"/owners/543"
}
}
/owners/543 % supports GET, PUT, DELETE
{
"owner":{
"name":"Norm Ferguson",
"pets":[
"/pets/123"
]
}
}
We have a nice and clean REST API. It’s at least level 3 in the Richardson maturity model. Maybe higher.
Our customer is happy, the API is quite popular, but here it comes. A change request. We need to support more owners for a pet. No problem, we can change the resource representation like this.
{
"pet":{
"name":"Pluto",
"owners":[
"/owners/543"
]
}
}
But it’s a backward incompatible change and we do not want break applications written for the old API. The only option is to create a new version. But how? We have several possibilities.
Version as part of the URI
The Apigee book recommends to add the version to the URI. Something like
/v2/pets/123 % supports GET, PUT, DELETE
{
"pet":{
"name":"Pluto",
"owners":[
"/owners/543"
]
}
}
You see, I have added version number to the URI. So the clients that support the new API can use v2. But wait, what happens, if a client navigates from v2/pets to owners and back to the pets resource? He will end-up in the first version of the API! We do not want that. It’s inconsistent. To fix this problem, we have to update versions of all resources in our application! The trouble is that we also have to change all the URIs in the resource representations. So the new API would look like this
/v2/pets/123 % supports GET, PUT, DELETE
{
"pet":{
"name":"Pluto",
"owner":"/v2/owners/543"
}
}
/v2/owners/543 % supports GET, PUT, DELETE
{
"pet":{
"name":"Pluto",
"owners":[
"/v2/owners/543"
]
}
}
All the resource URI have changed and also all the URIs in the JSON. This change might be easy to implement in a small homogeneous system but will be quite complicated in a system which is composed from several complex modules written in different programming languages. So if you have a REST API which is interconnected by hyperlinks, URI-based versioning will be quite costly. What are the alternatives?
Version as request parameter
/pets/123?v=2
I do not like this one. It’s basically a variant of URI-based versioning. Yes, a client can add the version parameter to every request. But it goes against the notion of hypermedia. The client should not be forced to change the request URI. He should be able to navigate using the links contained in the resource representation.
Version in the header
The other option is to use an HTTP header. The client can pass x-pet-version=2
header with every request and the server will know which version to return. The client usually already uses an HTTP Accept header for content negotiation so one more header should not be a problem. There are several issues with this approach.
The first one is the default value. It’s not clear, what to do, if the client does not send the header. If we serve him the last version, we will break such clients by every API update.
It is also less comfortable to browse older version of such API using the browser.
Version in the domain name
My favorite approach is to use different domain name for each version. Something like http://v2.petclinic.example.org/pets/123
. It’s easy to use, easy to implement and easy to change the version. If you use relative URIs, you just need to change the resource that needs to be changed and the rest works automatically. Theoretically, you can even use old code-base on the old domain name and new code-base for the new version.
I am just surprised that this approach is not used more often. If you check popular APIs they usually pick the URI-based approach or HTTP headers. Is there some problem with this approach I do not see?
Sources:
Web API Design: Crafting Interfaces that Developers Love
The REST API Design Handbook