API Versioning – So many opinions!

Firstly, I don’t want this post to be another rant about what you should do or shouldn’t do when it comes to versioning your API.

What I’m discussing here is what I did with our API, why I did it, and (3 years down the track) what I am planning on changing due to current usage patterns.

What was available on the market?

When I was doing my research on how we could version our API, there were 3 main options, each seeming to have their own pros and cons:

1. Version using the URL

HTTP 1.1 GET /v1/messages

2. Version using a URL query parameter

HTTP 1.1 GET /messages?v=1

3. Version using headers (with vendor specific mime types)

HTTP 1.1 GET /messages
Accept: application/vnd.company.resource-v1+json

 and the winner was..

After much debate (mostly internal) I decided to choose door number 3, versioning with VSMT (vendor specific mime types).

For those interested, I go on about it in detail here: http://bit.ly/TDNzLN

The main reason I chose VSMT and versioning was so that we could change the versions of each resource representation independent of the entire API, and so that versions were not tied to the URL and therefore not polluting the code.

e.g. let’s say we have a contact object with details of a person:

=== REQUEST ===>
HTTP 1.1 GET /contacts?firstName=Neil
Accept: application/vnd.company.contact-v1+json

<== RESPONSE ===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.contact-v1+json
{
   "id" : "1",
   "name" : "Neil Armstrong",
   "url" : "/contacts/1"
}

In my mind, if I wanted to update the representation of this contact, I could simply do so and change the version.

e.g. Let’s add an email and website to our contact.

=== REQUEST ===>
HTTP 1.1 GET /contacts?firstName=Neil
Accept: application/vnd.company.contact-v2+json

<== RESPONSE ===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.contact-v2+json
{
   "id" : "1",
   "name" : "Neil Armstrong",
   "email" : "neil@nasa.com",
   "website" : "http://www.nasa.com",
   "url" : "/contacts/1"
}

What’s the problem with this approach?

Well, what I’ve found is two things:

1. Users don’t like adding extra headers to their requests

In any language, it’s relatively easy to add HTTP headers. The problem is that users will often either not get how to do this, or not see the benefit in it.

I’ve had more complaints from users about adding custom headers than any other part of our API.

Also, if your argument that using versioning in the URL pollutes your code with versions so you should put it in the headers, you’re just moving your problem!

=== REQUEST ===>
HTTP 1.1 GET /v1/contacts?firstName=Neil

<== RESPONSE ===
HTTP/1.1 200 OK
{
   "id" : "1",
   "name" : "Neil Armstrong",
   "url" : "/contacts/1"
}

Is the same as…

=== REQUEST ===>
HTTP 1.1 GET /contacts?firstName=Neil
Accept: application/vnd.company.contact-v1+json

<== RESPONSE ===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.contact-v1+json
{
   "id" : "1",
   "name" : "Neil Armstrong",
   "url" : "/contacts/1"
}

You’ve just shifted your coding of the versioning from line 1 to line 2, it’s still in the code though.

2. Developers don’t like maintaining lots of versions of the schema

It may be different in your development team, by the devs that I work with are quite happy to continue on version 1 of the schema ensuring that backwards compatibility is always there.

In the example above, as long as we didn’t remove the “Name” field, v1 can be used forever more.

Most users will parse your API responses using json_decode style functions anyway.

$json = json_decode($response);

$name = $json->{'name'}; //Neil Armstrong
$url = $json->{'url'}; // /contacts/1

//do something useful

If you now add the email and website elements to this version, the existing code will have no issues at all, so adding a new schema version is simply a headache for your developers and your users.

What I’m planning on changing…

Based on the users feedback, and my annoyances every time I’ve needed to build something with the our API, I’ll be changing the following:

1. Support default media types as well as VSMT

As a lot of users don’t understand how to use VSMT, I’m going to implement support for default mime types as well as VSMT.

I got this idea off GitHub (https://developer.github.com/v3/media/) so thanks guys!

e.g. Instead of only supporting:

HTTP 1.1 GET /contacts?firstName=Neil
Accept: application/vnd.company.contact-v1+json

I’m going to also allow:

HTTP 1.1 GET /contacts?firstName=Neil
Accept: application/json

This will simply return the latest version of the resource requested. Users will need to be aware that this can change in the future at any time, but we’ll support backwards compatibility as much as possible and send notifications when we are making major changes.

2. Allow content type requests on the URL

Again, as a lot of users are complaining about all these extra headers, I want to make things simple and allow the extension of the URL to support the mime type.

e.g. Instead of only supporting:

HTTP 1.1 GET /contacts
Accept: application/vnd.company.contact-v1+json

I’m going to also allow:

HTTP 1.1 GET /contacts+json

This change is based on the premise above where it will simply return the default version of the resource in the format requested.

Keep the users happy, and they’ll keep using your API

The whole premise of this comes back to asking your users what they want. They are the ones who are using the service, and if they don’t like it, they’ll go somewhere else.

Keep them happy, and they’ll keep using your API instead of someone else’s.

Advertisements

2 thoughts on “API Versioning – So many opinions!

  1. Nice post Jordy. Mandatory spelling correction = alot = a lot. I had a think about this a couple of years ago when I was looking at building an API for Documentum calls to return JSON. I think I spent most of my focus on the application side though and finding ways to validate the schema. Your learning on how people are supporting backward compatibility is probably the interesting part for that. Cheers!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s