dnlgrv

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.