Versioning APIs in Azure Functions

There are multiple ways to deploy REST APIs in Azure:

  • App Services
  • Containers (AKS, ACI)
  • Azure Functions

This post focus on a solution using Azure Functions.

Why Azure Functions?

When starting a new project and/or trying something new Azure Functions is the quickest go to market option. No need to provision servers/clusters. Low financial commitment due to micro-billing, saving $$$ if the project fails.

Versioning

On mid-long term versioning is a subject that needs to be addressed when developing APIs. How new versions will be deployed? How customers will discover new versions?

The most common API versioning patterns are:

  • Request header 
    /api/resource
    With header “Accept: application/vnd.company.myapp-v2+json” for instance
  • URL
    /api/v2/resource
  • Query String
    /api/resource?version=2

Choosing a pattern is an opinion-based decision which is not the scope of this article.

Versioning in source code repository

How API versions will be organized in code repository is another decision that needs to be made.

  • Single repository
    A single repository where versions are organized using namespaces (or package names), deployed as a single unit. It is a good starting point, but not recommended in complex or mid-long term projects as it makes maintenance harder. Making a change in version 3 might introduce bugs to other versions.
  • Different branches of a single repository
    Each version is contained in a branch (version-1, version-2, etc.), deployed in isolation. Fixing a bug in multiple versions can be addressed by merging or cherry-picking changes into multiple branches.
  • Different repositories
    Using a different code repository per major version. It makes sense only if a complete rewrite happened between major releases (v1 in Java → v2 in C#, etc). Handling minor versions should follow one the options above.

Versioning in Azure Functions

The simplest pattern to implement versioning in Azure Functions is using URLs. The HttpTriggerAttribute allows the definition of a custom route where the expected version is set.

// Version 1 of get devices
[FunctionName(nameof(V1List))]
public static IEnumerable<DeviceModel> V1List(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/devices")]HttpRequest req, ILogger log)
{
}
// Version 2 of get devices
[FunctionName(nameof(V2List))]
public static IEnumerable<DeviceModel> V2List(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "v2/devices")]HttpRequest req, ILogger log)
{
}

When deploying each version in isolation a router component is required to redirect requests to the correct API endpoint.

                     |R| --------> v1 Function App
|O|
/api/v2/devices --> |U| --------> v2 Function App
|T|
|E|
|R| --------> v3 Function App

The router component can be implemented in Azure using different services, such as:

A sample implementation of API versioning using URL, query string and/or request headers based on single repository can be found at https://github.com/fbeltrao/Samples/tree/master/azure-functions-versioning.