Versioning your HTTP API
So you’ve made an API, congratulations! Whether you used Rails, NodeJS, or Phoenix, you’ve got something out there and people are making HTTP requests. Now comes the interesting part: making changes to something people are using.
Making changes
We’ve all been there. You’re using a public API one day, and the next day it no longer works. The request you were making yesterday isnt’t returning the same response today. There’s probably nothing more frustrating than your service no longer working because the third-party API you were relying upon changed from under you.
“If I ever make an API, I’d never do that.” you said. Well, if you want to be true to your word, you’ll need to version your API.
Deciding how to version your API
You need to decide how you’re going to let consumers of your API choose which version they want to use.
A lot of developers tend to go with putting the version in the URL, so your
URLs end up looking like https://api.myservice.com/v1/resource
. This is
perfectly fine, and better than not versioning your API, but it’s not my
preferred method.
I prefer to have consumers specify which API version they want through
headers (specifically the Accept
header). There are a few reasons I
prefer this method:
- Providing a default version is easier (for those who don’t specify a version)
- Consumers can change the version at a header level, without having to update URLs in their application code
- I think the URLs look nicer without the version in them (call me shallow)
Media Types
You’ve probably seen some Media Types before, such as application/html
and
application/json
. It’s perfectly acceptable to create your own too! This is
also how consumers of your API could request a version of your API, through the
media type (Accept header).
For my APIs I ask consumers to provide an Accept
header in this format:
application/vnd.yourservice.v1+json
. The +json
is optional, but if you were
going to provide different response types (XML for example), your consumers
could use +xml
. As you can probably tell, all you would need to do (as a
consumer of the API) is change your v1 to v2, and your request would get a
different version of the API.
Headers are an excellent way for consumers of your API to provide additional
information to your API, without it needing to go in the URL. That way they
could request /resource/123
, but using headers, use different versions of
your API.
GitHub have a very good article on using Media Types to determine the API version.
Sensible defaults
One thing you should always do, is provide a default version of your API. That is, if someone doesn’t specify a version, their request should go to the default version. However, you should recommend to your consumers that they specify the version in their requests, because if you (the API owner) change which version is the default, then it’s as if you didn’t have versioning at all.
If possible, give your consumers advance warning that you are going to be releasing a new version of your API. You should give them enough time to evaluate it and make the necessary changes before you change the default.
You typically want the default to be the latest version of your API.
Routing requests
So now for some practical advice, how to actually use the Accept
header in
your application to route requests to the correct place. I can’t cover every
library, framework, or language out there, so I’m going to go over the two I’m
most familiar with.
Ruby on Rails
See Versioning your HTTP API - Ruby on Rails example.
Elixir
See Versioning your HTTP API - Elixir example.
Wrapping up
That was a long one, so I’m going to summarise:
- Version your API
- Consumers can request the version through headers, not just the URL
- Always provide a default version (most of the time it’s the latest version)
- Give your consumers notice if you’re updating the default version
Hopefully this will keep your API going strong, and you’ll be happy you stuck to your word.
If I ever make an API, I’d never do that.