Demystifying API Versioning: Strategies for Web APIs and Minimal APIs

Today, in this blog post, I am excited to cover the implementation of API versioning for both standard web API and minimal API. API versioning plays a crucial role in maintaining the stability and compatibility of an API once it is deployed and clients start using it.

Once an API’s functionality is established and clients rely on it, making abrupt changes can disrupt their expectations. This is where API versioning comes in, allowing us to introduce changes to a public-facing API while still supporting multiple versions simultaneously.

Introduction to API Versioning

To achieve effective API versioning, it’s essential to adopt a versioning pattern that is easy for clients to accommodate.

In this blog post, I will demonstrate four main strategies for versioning a web API: URL-based versioning, query string-based versioning, header-based versioning, and content type-based versioning.

By exploring these approaches, you’ll gain a comprehensive understanding of how to implement API versioning in your projects.

API Versioning for Standard ASP.NET Web API

Let’s start with the standard web API. For this purpose, we will need to include two NuGet packages: Microsoft.AspNetCore.Mvc.Versioning and Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.

These packages are specifically required for standard APIs. I will also address the NuGet package requirements for the minimal API later in this post.

The Setup

In the beginning, we have a standard out-of-the-box weather controller that returns weather information. Our goal is to implement API versioning for this controller.

Although you don’t necessarily need to start with a version right away, I personally recommend starting with version 1.0 from the beginning. This approach makes it easier to introduce newer versions later since clients are already aware of the versioning system from day one.

Configure Dependency Injection

Now, let’s configure the API versioning. To do this, we’ll use the AddApiVersioning method from the builder.Services object. This method allows us to configure how the API will be versioned.

The first step is to provide a default API version for cases where the client does not specify the version through the query string, header, or content type. The URL-based versioning is slightly different, as the version itself is included in the URL.

Therefore, if the version is not provided, it will result in a 404 error. However, for the query string, header, and content type-based versioning, it’s recommended to provide a default version.

To set up the default version, we can use the DefaultApiVersion property of the ActionValue object and set it to new ApiVersion(1, 0). Here, we use 1 as the major version and 0 as the minor version. Also, don’t forget to include the necessary namespace by adding using Microsoft.AspNetCore.Mvc.Versioning; to your code.

builder.Services.AddApiVersioning(setup => {
    setup.DefailtApiVersion = new ApiVersion(1, 0);
    setup.AssumeDefaultVersionWhenUnspecified = true;
    setup.ReportApiVersions = true;
});

Assuming the default version when unspecified is the next step. We can accomplish this by setting AssumeDefaultVersionWhenUnspecified to true in the setup object. This ensures that if the client does not specify a version in the query string, header, or content type, the default version will be assumed.

To report the API versions to the user, we can set ReportApiVersions to true in the setup object. This way, the client will be aware of the supported versions of the API.

Additional properties

Additionally, we can specify the API version reader strategy by using the ApiVersionReader property of the setup object. By default, it uses the query string as the version reader.

However, since we want to focus on query string-based versioning in this blog post, there’s no need to specify the version reader explicitly. The below code adds the attribute necessary for the controller to assign a version.

[ApiController]
[ApiVersion("1.0")]
public class WeatherForecastController : ControllerBase
{
}

Running checks

Once the configuration is complete, you can run the application and test it using a tool like Postman. When you execute the API without specifying a version, you should receive a response with the default version.

Supporting multiple versions

Now, let’s move on to supporting multiple API versions. To create additional versions, we can use the AddApiVersioning method multiple times with different parameters. This allows us to configure and handle different versions of the API concurrently.

To handle conflicting method definitions when using Swagger, we need to use the MapToApiVersion attribute on the respective actions. This attribute ensures that the correct action is selected based on the specified API version.

When deprecating older versions, it’s essential to inform clients about their deprecation. One way to achieve this is by adding a custom response header, such as X-API-Deprecated, which indicates that a particular version is deprecated. Clients can then check this header and take appropriate action to update their integrations accordingly.

Minimal API Versioning

Now, let’s shift our focus to implementing API versioning for a minimal API. For minimal APIs, we only need to include the Microsoft.AspNetCore.Mvc.Versioning NuGet package. The configuration steps are similar to those for standard web APIs but with slight differences.

The setup for the API versioning support is exactly the same as the standard API. The only difference is configuring a version set and attaching the version set with an API.

var versionSet = app.NewApiVersionSet()
      .HasApiVersion(new ApiVersion(1,0))
      .Build();

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index => 
        new WeatherForecase
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, 55),
            summeries[Random.Shared.Next(summeries.Length)]
        )).ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithApiVersionSet(versionSet);

We can set up the default API version, assume the default version when unspecified, report the API versions, and specify the version reader strategy as needed.

Lastly, it’s important to leverage conventions to specify API versions and deprecations for minimal APIs. This allows for a more streamlined and concise way of handling versioning without explicitly configuring every route and action. By adhering to a consistent convention, we can ensure that our minimal API remains structured and easy to navigate.

Conclusion

In conclusion, API versioning is an essential aspect of maintaining a stable and evolving API ecosystem. By implementing one of the four versioning strategies (URL-based, query string-based, header-based, or content type-based) discussed in this blog post, you’ll be equipped to introduce changes to your APIs while supporting multiple versions simultaneously.

Whether you’re working with a standard web API or a minimal API, versioning ensures smooth transitions and backward compatibility for your clients, fostering a positive developer experience.

The YouTube video for this blog post is available here.