As APIs evolve, new features, bug fixes, and breaking changes become inevitable. Without a versioning strategy, modifying an existing endpoint can break existing clients, mobile applications, or third-party integrations.
API versioning enables multiple API versions to coexist, allowing consumers to migrate at their own pace while maintaining backward compatibility.
In this article, you'll learn how to implement API versioning in ASP.NET Core 11, explore common versioning approaches, and understand how to manage API evolution in production.
Note: This article focuses on implementation patterns and production practices. It does not include benchmark results because API versioning primarily affects maintainability and compatibility rather than raw performance.
Why API Versioning Matters
Suppose version 1 of an API returns:
{
"id": 1,
"name": "Laptop"
}
Later, version 2 introduces breaking changes.
{
"productId": 1,
"productName": "Laptop",
"price": 999
}
Without versioning, existing applications may fail because the response contract has changed.
Versioning allows both responses to remain available until consumers migrate.
Common API Versioning Strategies
| Strategy | Example | Best For |
|---|---|---|
| URL Path | /api/v1/products | Public REST APIs |
| Query String | /api/products?api-version=1.0 | Internal APIs |
| Header | api-version: 1.0 | Enterprise APIs |
| Media Type | application/json;v=1 | Advanced REST APIs |
URL versioning is the most common because it is simple, explicit, and easy to document.
Create the Project
dotnet new webapi -n ApiVersioningDemo
Install the versioning package.
dotnet add package Asp.Versioning.Mvc
Configure API Versioning
Register versioning services.
using Asp.Versioning;
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion =
new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
This configuration:
Uses version 1.0 by default
Reports supported versions
Supports requests without an explicit version (if configured)
URL Path Versioning
Create a versioned controller.
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("API Version 1");
}
}
Create version 2.
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("API Version 2");
}
}
Clients can now request:
GET /api/v1/products
GET /api/v2/products
Query String Versioning
Enable query string versioning.
builder.Services.AddApiVersioning(options =>
{
options.ApiVersionReader =
new QueryStringApiVersionReader(
"api-version");
});
Example request:
GET /api/products?api-version=2.0
This approach keeps the URL unchanged while allowing clients to specify a version.
Header Versioning
Read the version from a request header.
builder.Services.AddApiVersioning(options =>
{
options.ApiVersionReader =
new HeaderApiVersionReader(
"api-version");
});
Example:
GET /api/products
api-version: 2.0
Header versioning is common in enterprise environments where clean URLs are preferred.
Deprecating an API Version
Mark an older version as deprecated.
[ApiVersion("1.0", Deprecated = true)]
Clients receive version information through response headers when ReportApiVersions is enabled, helping them plan migrations.
Version-Specific Endpoints
Some endpoints may exist only in newer versions.
Version 1:
[HttpGet]
public IActionResult GetProducts()
{
return Ok();
}
Version 2:
[HttpGet]
public IActionResult GetProducts()
{
return Ok(new
{
Version = "2",
SupportsFiltering = true
});
}
New functionality can be introduced without breaking existing clients.
API Documentation
If you're using Swagger/OpenAPI, generate separate documentation for each API version.
Example structure:
/swagger/v1/swagger.json
/swagger/v2/swagger.json
Separate documentation makes it easier for consumers to understand the capabilities of each version.
Join the conversation! Your thoughts help the community grow.