Versioning Your APIs

Introduction

If you’ve ever written an API and used it in a real-world application, you’ll discover the need to make changes to enhance your software.  The problem with changing an API is that once the interface has been published and used by other applications, the end points cannot be changed.  The best way to deal with this problem is to version your API so that a new version can have different end points.  This will give consumers time to change their code to match the new versions.

One sensitive consumer of API data is the mobile application.  If your company produces mobile applications, then you’ll need to deploy the new mobile app to the app store after the API has been deployed and is able to consume requests.  This creates a chicken or the egg problem of which should go first.  If the API is versioned, then it can be deployed in advance of the mobile application being available for download.  The new mobile app can use the new version of the API while the older mobile app versions still consume data from the previous versions of your API.  This can also avoid the problem of forcing your end users to upgrade ASAP.

Versioning Method 1

To version your API, there is an obvious but painful method of versioning.  This involves using a feature in IIS that allows multiple applications to be created under one website.  The process is to make a copy of the previous version of your API, then make changes to the code to represent the next version of the API.  Next, create a new application in IIS, say “V2.0”.  Then the path to your API will be something like “myapi.com/V2.0/controllername/method”.

Here is a list of the drawbacks to this method:

  • Deployment involves the creation of a new application every time a new version is deployed.
  • Any web.config file in the root directory of IIS would be inherited by all applications.
  • Keeping multiple versions of code becomes difficult to track.
  • Continuous integration becomes a headache because each version will need a deployment.

Several of these issues can be “fixed” by using creative merging/splitting of branches.  The inheritance problem can be fixed by leaving the root directory empty.  The creation of new applications under IIS can be automated through Powershell scripting (deployment process can check if app exists and create it if it doesn’t).

Versioning Method 2

A NuGet package that can be added to your solution to allow automatic versioning of controllers (here’s the package for .Net Core).  There is a great blog post on this package here.  I looked over the blog post and decided to do a little testing of my own.  I tested a sample Web API project for .Net Core to see if I could get the same results as duplicating projects and installing them as applications under IIS.  This is what I came up with for my version 2 controller:

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace WebApiVersionedSample.Controllers
{
  [ApiVersion("2.0")]
  [ApiVersion("2.1")]
  [Route("v{version:apiVersion}/Values")]
    public class Values2Controller : Controller
    {
      [HttpGet, MapToApiVersion("2.0")]
      public IEnumerable GetV20()
      {
          return new string[] { "value1 - version 2", "value2 - version 2" };
      }

      [HttpGet, MapToApiVersion("2.1")]
      public IEnumerable GetV21()
      {
        return new string[] { "value1 - version 2.1", "value2 - version 2.1" };
      }

      [HttpGet("{id}", Name = "Get"), MapToApiVersion("2.0")]
      public string Get20(int id)
      {
          return $"id={id} version 2.0";
      }

      [HttpGet("{id}", Name = "Get"), MapToApiVersion("2.1")]
      public string Get21(int id)
      {
        return $"id={id} version 2.1";
      }
    }
}

As you can see, I set this up to accept version 2.0 or version 2.1, and I removed the “api” default path.  If you specify version 2.0, your consumer application can only see the GetV20 method for a get operation and your application will see Get20(int id) for any get method that passes and integer id variable.  In my sample code, I only printed the version number to show what code was executed when I selected a particular version.  Normally, you’ll call a business class from your method and that business class can be shared between two or more versions if the functionality didn’t change.  If, however, you have different logic in your business class between version 2.0 and version 2.1, then you’ll need to create another business class to call from your get method for version 2.1 and leave your version 2.0 untouched.

If you want to keep things simple, you can start a new version by creating new controllers for each endpoint and just add the version number to the end of the name.  Then you can change any one or more get, post, put or delete to conform to your new version.  Just be aware that this logic will need to continue into your business classes if necessary.

For my version 1.0 controller, as an example, I used the ValuesController object with an attribute at the top like so:

[ApiVersion("1.0")]
[Route("v{version:apiVersion}/Values")]
public class ValuesController : Controller

The “Route” shows how the version number precedes “/Values” controller name.  To access this in your browser hit F5 and change the version number.

Example: “http://localhost:64622/v1.0/values” or “http://localhost:64622/v2.1/values”.

To change your startup browser location, expand your properties and double-click on the launchSettings.json file:

Now you can change the application url and the launchUrl:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:64622/",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "v1.0/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebApiVersionedSample": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "v1.0/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:64623/"
    }
  }
}

This method of versioning your API has a few advantages over the previous method:

  • Deployments are set.  Add new version code and re-deploy.
  • No need to keep multiple copies of source code.
  • No IIS changes are necessary.

There are some potential pitfalls that developers will need to be aware of.  One complex situation is the possibility of a database change.  This can ripple down to a change to your Entity Framework POCOs, and possibly your context object.  If you are cautious and add non-breaking changes (like adding a new field), then you can change your database repository code without breaking your previous versions.  If you have breaking changes (such as a change to a stored procedure), then you’ll need to get creative and design it so both the old version and new version of your code still work together.

Where to Find the Code

You can download the sample code used in this blog post by going to my GitHub account (click here).

Leave a Reply