Headers or URL?

In the context of REST APIs there is a lot of confusion around versioning. Some people recommend versioning REST APIs by adding the version in the URL. Others don’t version at all, and others recommend versioning REST APIs using headers.
Nothing bad so far, but I’ve personally seen a lot of different APIs, that serve different domains and purposes that have a v1 in the URL. Not that is very bad, but the same APIs reach their end of life, with the same v1 in the URL. It’s hard to believe that was no change there since their inception. If so, why add a version in the first place?

I think that these APIs end up with having the v1 forever because the consumer apps are also controlled by the same organization or teams.

This way, the teams just go and change the consumer apps to accommodate the API changes, and that is it. They don’t have to worry about breaking something, somewhere, because they know and have full ownership over those changes.

But what if you would have and API exposed for consumers and you have no control over the consumer code? Would this change something in terms of having v1 forever embedded in your endpoints? For sure it would! You would be forced somehow not to break your consumers, or constrained by some SLAs or legal stuff to ensure backwards compatibility, and to be very transparent about your API changes.

More than than, you will need to ensure that nobody gets hurt by calling your API, and you don’t end up answering to support calls from them. This will make you think about designing your API changes carefully.
Now, let’s see how we can version.

The usual approach to versioning( URL versioning)

The most common approach to versioning is through URLs. You have our endpoints in a form that includes a version like: https://myapp.com/api/v1/speakers.
In order to call anything from there, you need to add v1 and you are done.
If you are talking .NET and WEB API, is very easy to do that for every endpoint just by decorating the Controller with that version, and you are all set.

[Route("api/v1/speakers")]

The inventor of REST – Roy Fielding said in a tweet that:

Roy Fielding about v1
REST should imply evolvability

If you correlate this with what Tim Berners-Lee said then you might have no reason to version in URL. Not in the path anyways.

COOL URIs don’t change

Tim Berners-Lee, 1998

Why not change the URLS?

Think about it. Sometimes you navigate to websites and you know the URL by heart. URLs matters for SEO, need to be discoverable, express intent and describe the content.
If you suddenly decide to reorganize a website, and you go around and change everything in terms of URLs there a big chance to lose users. Their bookmarks won’t work anymore, and they won’t be able to find the content they are interested in. That’s when people usually give up and move on and forget about you.

If you decide(hopefully) to increase the version, you might break all your clients. And remember, you can’t force your clients to move to the new version when you want.
Surely you can’t say: “From now on, all my consumers will use v2”. It is physically impossible, since you don’t have control over them, and each of them has different priorities.

Suddenly, taking everyone on board for the new version becomes a long process. You need to ensure backwards compatibility for who knows how long (read it as forever) and documentation.

More than that, let’s take another scenario. Microservices and data transfer in Microservices with HttpClients. You usually have somewhere the URLs hardcoded, in a JSON or something or even inside libs with that are distributed through some package manager. If you change the URLs, to change the version to v2, you will need to find all those places in your code where you have it ‘hardcoded’. PS: you know you forget all the places.
Versioning your REST API in URL, to me is not the best idea.

Versioning trough headers

Now I know is not the most common way to do versioning but is my personal preference. Why? “Because cool URIs don’t change”.
Adding meaning and some kind of specificity to a request can be done in different ways, why do it through the URL itself. The URL should be self-describing, don’t pollute that with versioning-related pieces.

In my opinion, as long as that endpoint is very clear in terms of naming – is specific enough, gives a clear understanding of what is the resource it is supposed to handle you are all set.

You can add meaning to your requests very easily. You can add proper verbs, custom headers, to hydrate the request, to specify intent. Remember that a request should be understood in isolation, and HTTP is stateless.
This way you help a bit with content negotiation.
You add Accept header property to help the server understand what the client needs and expects, Content-Type to describe the request body, and so on.

In the same manner, you can add a custom header property that specifies what is the version that the client wants to call.

Using a library

Luckily there is an awesome library in .NET called Microsoft.AspNetCore.Mvc.Versioning that will speed up a lot of manual work. It will give you a lot of flexibility and config options, and your code won’t be a mess.

You will need to configure it in Startup.cs with the behavior you want.

services.AddApiVersioning(o => o.ApiVersionReader =
               new HeaderApiVersionReader("api-version"));


Then, you can go ahead and decorate the controllers with the right API version attribute.

[Route("api/speakers")]
    [ApiController]
    [ApiVersion("1.0")]
    public class SpeakersController : ControllerBase
    {
        //code removed for brevity
    }

Using simple attributes, you are now allowed to have 2 controllers side by side, that have the same route. One of them has the previous version, and the second the new version. Using the same URL and without any changes will respond correctly to requests, as long as the correct header is specified in the request.

    [Route("api/speakers")]
    [ApiController]
    [ApiVersion("2.0")]
    public class Speakers2Controller : ControllerBase
    {
    }

When you issue a request to the API, you will need to have api-version header with the correct value specified. api-version:1.0 or api-version : 2.0 Based on that, the request will hit a controller on another.
You are not limited to only this kind of configuration.
You can assume a default version if this header is not there, or you can enforce a query parameter for the version, configure default behavior and error pages, and so on.
Everything is configurable inside Startup.cs

When to increase the version?

There is no clear answer to this. It depends on the number of changes and their magnitude. Be pragmatic about it, and don’t take it to the extreme. An API version number is not the build number. Don’t do that.
How often do you increase the version number is a matter of API design first. Ask yourself a few questions:

How many consumers you have? In what way are these clients affected by your changes? What is the development speed for your consumers? Are there legal/contract matters? (Maybe they don’t have the same agility as you have)
Is it easy for you to maintain multiple versions of the same API?

In conclusion

Versioning REST APIs is not that hard, and REST itself is not bad. There is a certain amount of discipline to do this. You need to design the API with your consumers/clients in mind and take responsibility when you introduce changes.

If you find this intriguing, ping me on twitter. Let’s chat!