So, you are new to this team. And in one of the bi-weekly tech leads’ meetings, one thing leads to another and you casually bring up how the error messages on all the APIs are not consistent. You hear one of the engineers telling you that their pull requests to update the core error handler have always been kept on hold and have never been merged. Before you could ask for more information, you hear a few other audible nods. All those have one thing in common, they are all breaking changes. Breaking, for the downstream systems and/or breaking for the clients that consume these resources. This discussion makes way for one of the commonly contested aspects of API development: “versioning”.
I think the topic of API versioning is probably as old as or older than the topic of URL design for applications themselves. And probably a discussion that most teams involved in API development get involved in. What makes it more interesting is the fact that there is no right answer or approach that fits all use cases. And thus makes up for quite an interesting conversation.
Why would you need to version your APIs?
A versioning strategy allows clients to continue using the existing API and migrate their applications to the newer API when they are ready. If you need to make breaking changes you might continue to do so and migrate to this new structure only when all the essential stakeholders are ready for it.
While certain modifications like adding new routes or route parameters shouldn’t cause any problems. Changes that could affect existing clients or downstream systems that consume your API would mean that you would need to think about a strategy for versioning thoroughly. Like most cases, we have multiple ways to approach this.
“The most RESTful approach to versioning an API is to not version it at all.”
~(Old Asgardian Proverb)
Embedding API version in the URI?
This looks like the easiest way forward. However, this would be against the HATEOAS constraint [Hypermedia As The Engine Of Application State]. Adding a version number to the API would mean that the client is making an assumption of how an API would behave and would thus mean that the API is no longer opaque. If we go by the book, clients should be dynamic and only rely on the API responses [see. web browsers]. It might also mean that incrementing the API version would translate into branching the entire API resource.
Through content negotiation
Using the content negotiation strategy. This helps us by requiring only a single resource to represent the API instead or versioning the entire API and thus giving us more control over versioning. Also, we would not need to define custom routing rules, like we had to while using version as part of the URI path.
This approach is very straightforward and is also easy to set defaults to the latest version in case of missing query parameters.
Biggest challenge with this approach would be setting up routing.
This is another approach which might not be very popular with teams, but does the job. Sending version number as a custom header.
curl -H “accepts-version: 1.0”
Lets talk code
.net core like few other popular frameworks, offers versioning through content negotiation via custom libraries.
Add the nuget package ‘Microsoft.AspNetCore.Mvc.Versioning’ and update the startup.cs accordingly.
We can then go ahead and add a class level attribute of ‘ApiVersion’ on our BaseController.
Now when you compile and send a request to this endpoint. You might notice a change in the headers. Something like this:
api-supported-versions -> 1.0
Just for kicks if you send in a different header. Like this:
curl -i -H "Accept: application/json;v=2.0"
You might notice that the same call might throw a ‘400 Bad request’, stating that the error code as ‘UnsupportedApiVersion’ . We don’t have an apiversion attribute corresponding to version 2.0 on this endpoint. But if we did, we would have been able to request that.
Which versioning strategy to use?
Which approach to use depends on the project and the intent. Do you want versioning that’s more explicit to clients or one that is more flexible and clean? Are you expecting the interface to be stable or have breaking changes? Planning and thinking through all the use cases for your API in advance can reduce the need to make breaking changes down the road.