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.
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
With header “Accept: application/vnd.company.myapp-v2+json” for instance
- Query String
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
public static IEnumerable<DeviceModel> V1List(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/devices")]HttpRequest req, ILogger log)
// Version 2 of get devices
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
/api/v2/devices --> |U| --------> v2 Function App
|R| --------> v3 Function App
The router component can be implemented in Azure using different services, such as:
- Azure Function Proxies, for a full serverless solution
- Application Gateway, adding Web Application Firewall protection
- API Management, adding multiple features for APIs development (security, request throttling, monetization, developer experience, etc…)
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.