When building RESTful APIs there usually comes a point where you need to make a breaking change and start to version your API. This post covers the commonly used options available, along with some examples of who uses each approach. I’ve attempted to be as impartial as I can. For those who are interested my personal thoughts are within the final section of the post.
I have consistently had conversations with other developers, who have a firm position stating “APIs shouldn’t be versioned”. They keep reiterate that if you have to version something is broken and it should be fixed inline. While I appreciate their enthusiasm and dedication to the ideology of API design, sometimes a breaking change leading to a new version of an API is better than leaving your clients with an API that doesn’t fell intuitive.
Consider your clients
My golden rule for API versioning (and design!) is “Make it as easy for your clients as you can”, imagine what it could be like to consume your own API…hopefully you do through testing. This should give you an idea of how clients will perceive your API. If something doesn’t feel intuitive or natural, reconsider the design, its much easier to make changes before anyone is consuming your services. Thinking of your clients, should be at the formost of your mind when your think about when designing/modifying/versioning an API. Always remember the consumers are key.
Common reasons for versioning
There are two main reasons I’ve had to version an API. The first is a breaking change in the domain model, the second is a change in API contract. They can be treated as separate concerns, however I doing so can easily confuse clients. This is especially true if one versioning mechanism is used for each approach (I’ve seen this and really wouldn’t recommend it)
From my experience and extensive reading these are main options that have been widely adopted, below I’ll give some examples of each approach, along with some examples of who’s using each approach.
HTTP GET: https://krismassey.com/api/v1/profile/
HTTP GET: https://krismassey.com/api/v2/profile/
Arguably the most widely used approach to API versioning management. The API is simple and easy for clients to understand, versions can be deprecated overtime by removing URLS (its important to inform clients well ahead of time so they can upgrade versions) One of the key considerations with this approach is some technologies/frameworks wont support this out of the box, and can be a pain to maintain if multiple version are used in this method. On the downside changing the URL feels like a change in contract, there is an argument that this is no bad thing as versioning should only take place when a breaking change is needed.
Using a query parameter has a large number of similarities to the use of URL versioning. It offers the benefit of making the query paramater required or optional depending on your needs. It can also allow for versioning to be added after your API is in the wild, if your thinking of this approach my recommendation would be to interprete a missing query param as the original version.
My personal recommendation would be to use one of the Header options below over this approach. My reasoning for this is the use of a query parameter can feel clunky from the clients perspective.
HTTP GET: https://krismassey.com/api/profile/<profile_id> Accept: application/vnd.krismassey.v2+json
This approach has some similarities to the query parameter method. With the main exception of the URL not containing any details about versions. From a URL contract perspective this feels cleaner, however price is the versioning is a little more abstract. Once again there are technology/framework considerations as some wont integrate nicely with custom Accept header values, altho these are generally few and far between.
- Who uses it
HTTP GET: https://krismassey.com/api/profile/<profile_id> Header: example-api-version: 1
There is very little between the Accept Header approach and this one. The main different is that a custom header can take any format you like, there are no restrictions as the definition is down to you. On the other side of the fence some technologes/frameworks wont offer any support for this.
If you have to run multiple APIs there could be a good reason to use a different approaches for each API. Amazon have done this with some of their APIs, personally for me it feels like a lack of consistency, however in larger organisations I’m not surprised it happens as keeping a standard across the board can be difficult. Especially when your APIs grow at a rate such as those within the AWS ecosystem.
Who uses it
There is no right or wrong approach to versioning APIs, do what you feel is right. A lot of purists will lean towards the URL or Accept header approaches. Personally I prefer the use of a Custom Header or the Accept Header, I’ve found support within frameworks is good and it feels right for my way of thinking.