DEV Community

Cover image for Real World Developer's Problems: API Versioning
Jonathan Brizio
Jonathan Brizio

Posted on

Real World Developer's Problems: API Versioning

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.

Discussion (19)

Collapse
cdimonaco profile image
Carmine Di Monaco

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.

Collapse
redfred7 profile image
Fred Heath

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.

Collapse
anduser96 profile image
Andrei Gatej

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

Collapse
redfred7 profile image
Fred Heath

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...

Collapse
lirantal profile image
Liran Tal

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:

Collapse
andychiare profile image
Andrea Chiarelli

> 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

Collapse
kayis profile image
K

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
Collapse
andychiare profile image
Andrea Chiarelli

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.

Thread Thread
kayis profile image
K

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.

Collapse
jasonmulligan profile image
Collapse
chchrist profile image
Tolis Christomanos

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...

Collapse
jasonmulligan profile image
Jason Mulligan

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

Thread Thread
chchrist profile image
Tolis Christomanos

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

Thread Thread
jasonmulligan profile image
Jason Mulligan

That's rich, since it's a spec.

Collapse
iancasarim profile image
ian .

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.

Collapse
steelwolf180 profile image
Max Ong Zong Bao

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

Collapse
mattdevio profile image
Matt G

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.

Collapse
lewiscowles1986 profile image
Lewis Cowles

I haven't used it enough to validate that statement

Collapse
lewiscowles1986 profile image
Lewis Cowles

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