loading...
Cover image for Real World Developer's Problems: API Versioning

Real World Developer's Problems: API Versioning

jonathanbrizio profile image Jonathan Brizio ・1 min read

The backend world is fascinating and full of new things to discover.
In the beginning, you can feel domed with all the information, so the best way to understand is used commons examples that apply these concepts.

When you're designing your backend, it is essential to have on mind some considerations. Think that your contract on the future would change and you need to maintain backward compatibility.

In some moment you API would be evolving, and you need to support the features they provide. Managing the impact of this can be quite a challenge when it threatens to break existing client integrations.

REST doesn’t specify any method of versioning, but the more common approaches are:

  • Specify the version putting it on the URI:
    https://api.example.com/v1/

  • Using a custom request header:
    Accept-version: v1

  • Alternatively, adding to the HTTP Accept header
    Accept: application/json; version=1

With these approaches, your challenge would be managing the entire code base with multiple versions of your resources. Content negotiation may let you prevent modify your entire collection of URLs, but you still have to deal with the all complexity of serving different versions of content.

Another approach can be to develop a consumer-driven contract that allows to consumer declare the data they are interested in consuming as part of a request. On this last case, you're delegating the responsibility to the client.

Whatever approach you take, need to be consistent and active governance over the evolving contract.

Comment here what another approach you recommend or know it to recommend us.

Posted on by:

Discussion

markdown guide
 

I think the Uri approach is the Best in the long term, it's straightforward in terms of clarity for the consumers, even without a full fledged framework, you can do the job easily.

I've done many apiis with both Go, with just chi as router and more recently, RoR. In both cases I had no problems at all.

The headers solution can be used in more bounded context, like internal communications between microservices, this approach lost clarity but can be more comfortable migrate between versions in complex sistems, just like changing a header, maybe with an environment variable or stuff like that.

 

Hi Jonathan, good post, this is a rarely-examined issue when creating REST APIs. My view is:

Specify the version putting it on the URI:
*api.example.com/v1/*

This approach isn't RESTful as we are having multiple URNs for the same resource, which breaks REST constraints for resources

Using a custom request header:
*Accept-version: v1*

Caching becomes difficult, so for that reason I reject this.

Adding to the HTTP Accept header
*Accept: application/json; version=*

I think this is the best approach because it separates versioning and media types without requiring an extra header and it keeps the URI clean. On the downside, it's more difficult to parse the Accept header and it requires more work server-side, but IMO the positives outweigh the negatives.

 

Could you please elaborate on why using a custom request header makes caching a difficult thing to achieve?

 

Hey Andrei, it's too long to properly describe in a reply but in a nutshell: if we use custom headers we would need to specify these in the Vary HTTP header, so that proxy caches would know where to find the version number, so that they can compose their cache keys. However, content negotiation using the Vary header is notoriously difficult and often leads to cache fragmentation, which is why it's best avoided. bizcoder.com/the-insanity-of-the-v...

 

An HTTP Accept Header is probably the most semantic way to convey the versioning intent.
I don't like the URL usage at all as that couples versioning to the URL, and the custom header is generally alright except it can be an issue in the wild web of proxies etc that would get in the way of that.

I wrote a couple of libraries that work together to achieve route-based API versioning for Express if anyone is needing that for Node.js based applications:

 

> REST doesn’t specify any method of versioning

The problem is that most of what we call RESTful API are not RESTful at all

A true RESTful API does not need versioning, as Fielding explains in this article

 

My point was that there is no need to anticipate such world-breaking changes with a version ID. We have the hostname for that. What you are creating is not a new version of the API, but a new system...

If you look at constraints of SemVer, a new version is basically that, a new system that doesn't try to be compatible with older clients.

If his point is "you do a new system, get a new hostname" the chain of arguments is simple.

  1. Do a new version
  2. Fielding says a new version is a new system
  3. Fielding says a new system needs a new hostname
  4. Put the version in the hostname
 

You missed the end of the sentence: "...but a new system with a new brand" :-)
It seems just a detail, but is fundamental, instead.

Fielding is not saying that a new version is a new system. He is talking about a totally different system, different just like dev.to and medium.com.

The point is: when do you really need a new system when your API is true RESTful?

Read the sentence just after the one you mentioned:

On the Web, we call that a new website. Websites don’t come with version numbers attached because they never need to. Neither should a RESTful API. A RESTful API (done right) is just a website

Basically, a REST client should only know the entry URI of your (true) RESTful APIs. If you change the signature or the organization of your APIs, your client should be able to discover them without need to specify a version.

Indeed, in a true RESTful context, each resource must have the URIs of any related resources. Again, the client should know just the initial URI of your APIs. The other URIs must be dynamically discovered: basically this is the meaning of State Transfer in the REST acronym.

I didn't miss it, I deliberately omitted it, because it's only semantics. Different system or version is just a question of perspective here.

The only thing Fielding has to say about this problem: Either you never break backward compatibility or you are not doing REST. If you want to do REST, create a new API every time you would break backward compatibility.

He's only unhappy with the fact that some people call these new non-backward compatible systems "new major version" and not "new systems".

GTA isn't GTA3 etc.

 
 

The problem is that to integrate with an evolvable API is too much work on the consumer especially when the features can change so often in this fast pacing world...

 

That means it was poorly designed and/or implemented. The man that defined the style says it's an anti-pattern.

Fielding's dissertation is open to so many interpretations...

 

Very cool article, thank you!

I found using the header is more clean overall.

I also had been using API Management tools, it helps a lot with versioning. LinkApi, for example, is free and very dev-friendly.

 

I think my end is more towards customer driven contacts through postman and OpenAPI spec.

 

You could use GraphQL as it basically eliminates this problem. I haven't used it enough to validate that statement, but that seems to be a hallmark of the platform.

 

I haven't used it enough to validate that statement

 

I really hoped you meant API in the broader sense. Learning how to manage & version public interface APIs goes a long way to understanding managing abstract service APIs