API versioning is often an afterthought during the development process when, in fact, it should be the foremost part of designing an API, for user consumption and ease of usability.
Being the API enthusiast that I am, I wanted to write up a quick article describing the various types of API versioning available at this time to better the API experience for end-users.
Off the bat, there are a few options that come to mind:
- Inline route versioning (my favorite).
- Query strings (sloppy).
- Accept headers (an afterthought, in my opinion).
The basic principle is this — to manage complexity and breaking changes within your API, always version your API (a.k.a. AVYA). Put simply, API versioning enables you to iterate faster when breaking changes are identified.
If you don’t want to version your API, or are afraid that it will add complexity during the build process, I’d recommend that you take into consideration the following:
- If users are experiencing issues with the output of your API post-release, will you eventually change the response data to accommodate those requests or leave them as-is and ignore user feedback?
- What if you have to remove an endpoint altogether from your API because it is no longer needed? Without proper versioning, users may still be hitting this endpoint, only to find that they receive a 404. Status codes like 404s are not exactly fun for developers to deal with, regardless of whether or not you update your documentation — it’s one extra step the developer is required to take.
- Your API now requires an additional key for a POST method. On your original API, you don’t require it; however, on your next iteration, the API will break if the new key is not passed.
- The route structure of your API is changing due to semantics and best practices. This is for the best; however, you’re going to leave users with an old API structure in the dust, or even worse, broken, if you don’t version out your API.
I could go on forever, but the gist of it is that you should always version your API to ensure that developers have a clear and concise way of communicating with an ever-changing and up to date API.
In my world, there are three possible ways to version an API properly. In order from my favorite to least, those include:
- URL, also known as route versioning (e.g.
/v1/:foo/:bar/:baz
). - Versioning via a custom header (e.g.
Accept-Version: v2
). - Query string parameter (e.g.
/:foo/:bar/:baz?version=v2
).
URL or route versioning is my personal favorite. It’s clear and concise to the user which API they are hitting and allows the user to reference the correct documentation.
Many frameworks support route-defined versioning, and when it doesn’t, you can simply structure your API so that it allows for route versioning. Because of this, it’s extremely easy to implement; however, it does add length to your REST API route, which is the downside.
Custom headers are next on my list because they do what they are supposed to — they specify the version of the API, and the server can respond appropriately with the correct response data as well as parse the incoming payload on the fly.
This might sound like an easy solution; however, consider developers who simply want to extract data via your open-source API without providing custom headers.
This, in particular, poses a problem as the user should not have to be forced to send a custom header to simply specify the version of the API. Additionally, it requires overhead on the server to parse prior to the response, which could add latency.
Last but not least, there’s query string versioning. This is known as bad practice by many developers; I often see it in production, and it makes me cringe.
It screams that the API development and, more importantly, required parameters and the schema were an afterthought. Stay away from this at all costs.
When in doubt, use URL/route versioning if you can. I promise that it will simplify your development process moving forward and keep developers who are using your API happy.
In future posts, I’ll outline how to structure versioning by hand with route definitions using a REST API with Express. In the meantime, please leave any questions or thoughts in the comments below.
Until next time. Happy coding!