API Versioning
SkillAI & modelsThis skill gives your AI working knowledge of API versioning for ASP.NET Core. Once added, it can help you add versions to an API, keep it working through breaking changes, and retire old versions cleanly. It also covers linking versioning with OpenAPI documentation.
Available today. Use it from your connected AI after setup.
No other account needed.
Add the skill, then ask your AI to add versioning to an existing API or plan a breaking change. Mentioning 'API version' in your request will bring these strategies into play.
Then ask your AI: use the API Versioning skill
What your AI can do with it
- Add versioning to an ASP.NET Core API using the Asp.Versioning library
- Choose between URL segment, header, or query string versioning
- Evolve an API safely when breaking changes are needed
- Plan and manage version deprecation
- Connect API versioning with OpenAPI documentation
What this skill tells your AI
The instructions your AI receives, as published by codewithmukesh/dotnet-claude-kit in skills/api-versioning/SKILL.md and read by ahel’s review.
Core Principles
- Version from day one — Adding versioning later is painful. Start with a version in the URL even if you only have v1.
- URL segment versioning is the default —
/api/v1/ordersis the most discoverable and cache-friendly strategy. - Never break existing versions — Add a new version for breaking changes. Deprecate the old version with a timeline.
- Version the API, not individual endpoints — All endpoints in a version group share the same version number.
Patterns
Setup with Asp.Versioning
// Program.cs
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
URL Segment Versioning (Recommended)
var v1 = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(1, 0))
.Build();
var v2 = app.NewApiVersionSet()
.HasApiVersion(new ApiVersion(2, 0))
.Build();
app.MapGroup("/api/v{version:apiVersion}/orders")
.WithApiVersionSet(v1)
.WithTags("Orders")
.MapOrderEndpointsV1();
app.MapGroup("/api/v{version:apiVersion}/orders")
.WithApiVersionSet(v2)
.WithTags("Orders")
.MapOrderEndpointsV2();
Header Versioning (Alternative)
options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
// Client sends: X-Api-Version: 2.0
Deprecating a Version
var v1 = app.NewApiVersionSet()
.HasDeprecatedApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.Build();
// Response headers will include: api-deprecated-versions: 1.0
Version-Specific Endpoint Groups
public static class OrderEndpointsV1
{
public static RouteGroupBuilder MapOrderEndpointsV1(this RouteGroupBuilder group)
{
group.MapGet("/{id:guid}", GetOrderV1);
group.MapPost("/", CreateOrderV1);
return group;
}
private static async Task<Results<Ok<OrderResponseV1>, NotFound>> GetOrderV1(
Guid id, ISender sender, CancellationToken ct)
{
// V1 response shape
var result = await sender.Send(new GetOrder.Query(id), ct);
return result.IsSuccess
? TypedResults.Ok(result.Value.ToV1())
: TypedResults.NotFound();
}
}
public static class OrderEndpointsV2
{
public static RouteGroupBuilder MapOrderEndpointsV2(this RouteGroupBuilder group)
{
group.MapGet("/{id:guid}", GetOrderV2);
group.MapPost("/", CreateOrderV2);
return group;
}
private static async Task<Results<Ok<OrderResponseV2>, NotFound>> GetOrderV2(
Guid id, ISender sender, CancellationToken ct)
{
// V2 response shape — includes new fields
var result = await sender.Send(new GetOrder.Query(id), ct);
return result.IsSuccess
? TypedResults.Ok(result.Value.ToV2())
: TypedResults.NotFound();
}
}
Anti-patterns
Don't Version Individual Endpoints
// BAD — inconsistent versioning within a group
app.MapGet("/api/v1/orders", ListOrdersV1);
app.MapGet("/api/v2/orders/{id}", GetOrderV2); // V2 only for this endpoint?
// GOOD — version the entire group
app.MapGroup("/api/v1/orders").MapOrderEndpointsV1();
app.MapGroup("/api/v2/orders").MapOrderEndpointsV2();
Don't Use Query String Versioning as Default
// BAD for REST APIs — version hidden in query string, not cache-friendly
GET /api/orders?api-version=2.0
// GOOD — version in URL, discoverable and cacheable
GET /api/v2/orders
Decision Guide
| Scenario | Recommendation |
|---|---|
| New public API | URL segment versioning from day one |
| Internal API between services | Header versioning (cleaner URLs) |
| Breaking response shape change | New version |
| Adding new optional fields | Same version (backwards compatible) |
| Deprecating a version | Mark deprecated, set sunset date, document migration path |
Signals
- GitHub stars
- 710
- Forks
- 167
- Last commit
- Aug 2026
Others that do the same job
Advanced
- Catalog kind
- skill
- Gateway key
api-versioning- Source
- github.com/codewithmukesh/dotnet-claude-kit