API Versioning Best Practices in .NET 8

Hi Everyone, Today, we will understand the concept of API Versioning. To Follow the REST Standard guidelines for API development, check out this blog rest-api-standard-for-apis-development.

In this article, we will create the REST API supporting multiple versions in .NET 8.

Introduction

API versioning is the practice of managing changes to your API in a structured and predictable manner. It helps maintain compatibility with existing clients while enabling the introduction of new functionality. In this article, we'll explore what API versioning is, why it's important, and various strategies for implementing it effectively.

Why is API Versioning important?

  1. Backward Compatibility: Ensures existing clients can continue to use the API without breaking when new changes are introduced.
  2. Continuous Improvement: Allows developers to make improvements or add new features to the API without disrupting current users.
  3. Smooth Transition: Provides a clear path for clients to transition from one version of the API to another.
  4. Error Management: Helps identify issues more effectively when problems arise from changes.

Key strategies for API versioning

There are several common strategies to implement API versioning, each with its own advantages and trade-offs.

  1. URI Path Versioning: In this strategy, the version number is included in the URI path, for example.
    • Version 1: https://api.example.com/v1/users
    • Version 2: https://api.example.com/v2/users
  2. Query Parameter Versioning: In this approach, the version number is included as a query parameter in the URL. For example.
    • https://api.example.com/users?version=1
    • https://api.example.com/users?version=2
  3. Header Versioning: Here, the version number is included in the HTTP headers, for example.
    GET /users HTTP/1.1
    Host: api.example.com
    X-API-Version:2
    
  4. Content Negotiation (Media Type Versioning): This strategy uses the Accept header to specify the version of the API, for example.
    GET /users HTTP/1.1
    Host: api.example.com
    Accept: application/json; version=1 
    

Sample project in .NET 8 (C#) Web-API

Step 1. Install the Necessary Package Add the Microsoft.AspNetCore.Mvc.Versioning package.

dotnet add package Microsoft.AspNetCore.Mvc.Versioning

Step 2. Configure API Versioning in Startup.cs Modify the Startup.cs file to include API versioning.

using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    // Define Swagger documents for different versions
    options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
    {
        Version = "v1",
        Title = "My API v1",
        Description = "API documentation for version 1"
    });
    options.SwaggerDoc("v2", new Microsoft.OpenApi.Models.OpenApiInfo
    {
        Version = "v2",
        Title = "My API v2",
        Description = "API documentation for version 2"
    });
    // Use the ConflictingActionsResolver workaround
    options.ResolveConflictingActions(apiDescriptions =>
    {
        // Your conflict resolution strategy here
        // Example: Choose the first action
        return apiDescriptions.First();
    });
    // Add more versions as needed
});
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0); // Default version: 1.0
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
    // Combine multiple versioning schemes
    options.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader("version"),
        new UrlSegmentApiVersionReader(),
        new HeaderApiVersionReader("X-API-Version"),
        new MediaTypeApiVersionReader("version")
    );
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        // Define the endpoints for each version
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
        options.SwaggerEndpoint("/swagger/v2/swagger.json", "My API v2");
    });
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 3. Create the Users Controller Implement a UsersController that supports multiple versioning methods. Start with versions 1.0 & 2.0.

using Microsoft.AspNetCore.Mvc;
namespace RestAPIVersioning.Controllers
{
    [ApiVersion("1.0")]
    [ApiVersion("2.0")]
    [ApiController]
    [Route("api/v{version:apiVersion}/users")] // URI Versioning
    [Route("api/users")] // QueryString & Header & MediaType Versioning
    public class UsersController : Controller
    {
        [HttpGet]
        // Version 1.0 is optional and Api can work without Here
        [MapToApiVersion("1.0")]
        public IActionResult GetUsers()
        {
            var users = new[] { new { Id = 1, Name = "John Doe" } };
            return Ok(users);
        }
        [HttpGet]
        [MapToApiVersion("2.0")]
        public IActionResult GetUsersV2()
        {
            var users = new[] { new { Id = 1, Name = "John Doe", Email = "[email protected]" } };
            return Ok(users);
        }
    }
}

Step 4. Test Your Versioned API: Run your application and access the different versions of your API using various versioning methods. You can test the API with PostMan or Swagger.

  • URI Versioning: http://localhost:<port>/api/v1/users and http://localhost:<port>/api/v2/users
  • Query String Versioning: http://localhost:<port>/api/users?version=1 and http://localhost:<port>/api/users?version=2
  • Header Versioning: Use a tool like Postman to set the X-API-Version header to 1 or 2 and access http://localhost:<port>/api/users
  • Media Type Versioning: Set the Accept header to application/json; version=1 or application/json; version=2 and access http://localhost:<port>/api/users
    API

Pros & Cons
 

Versioning Type Pros Cons
Path Versioning Easy to implement and understand. This can lead to redundancy in URIs.
  Clearly visible and explicit in the URL. Harder to manage when the number of versions grows.
Query Parameter Easy to implement and integrate with existing URLs. Less visible than path versioning.
  Less cluttered URIs. May not align with RESTful principles.
Header Versioning Clean URLs without any versioning information. Less visible and discoverable.
  More flexible for clients. Requires clients to handle headers properly.
Media Type Versioning Adheres to RESTful principles. More complex to implement.
  Provides more flexibility for clients. Not as obvious as URI path versioning.


Best practices for API Versioning

  1. Communicate Clearly: Make sure your clients are aware of any changes and how to adapt to new versions.
  2. Deprecate Responsibly: Give clients sufficient time to migrate to new versions before deprecating an older version.
  3. Document Thoroughly: Provide detailed documentation for each version, including changes, deprecated features, and migration paths.
  4. Monitor Usage: Track which versions are being used to understand the impact of changes and guide future development.
  5. Version Incrementally: Use semantic versioning to clearly indicate backward-compatible changes (e.g., v1.1 vs. v2.0).

Conclusion

API versioning is crucial for maintaining a healthy, scalable API that can evolve over time without disrupting existing clients. Choosing the right versioning strategy depends on your specific use case, your audience, and your development practices. Whether you opt for URI path versioning, query parameters, headers, or content negotiation, the key is to ensure clear communication, thorough documentation, and a smooth transition path for your clients.