arch-check
Architecture conformance check: verifies an existing codebase against its declared…
API versioning strategies for ASP.NET Core. Covers Asp.Versioning library, URL segment, header, and query string strategies, version deprecation, and OpenAPI integration. Load this skill when adding versioning to an API, evolving an API with breaking changes, or when the user
$ npx -y skills add codewithmukesh/dotnet-claude-kit --skill api-versioning --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/api-versioningContext preview
The summary Claude sees to decide when to auto-load this skill.
API versioning strategies for ASP.NET Core. Covers Asp.Versioning library, URL segment, header, and query string strategies, version deprecation, and OpenAPI integration. Load this skill when adding versioning to an API, evolving an API with breaking changes, or when the user
name: api-versioning description: > API versioning strategies for ASP.NET Core. Covers Asp.Versioning library, URL segment, header, and query string strategies, version deprecation, and OpenAPI integration. Load this skill when adding versioning to an API, evolving an API with breaking changes, or when the user mentions "API version", "versioning", "v1/v2", "Asp.Versioning", "deprecation", "breaking change", or "backward compatibility".
1. **Version from day one** — Adding versioning later is painful. Start with a version in the URL even if you only have v1. 2. **URL segment versioning is the default** — `/api/v1/orders` is the most discoverable and cache-friendly strategy. 3. **Never break existing versions** — Add a new version for breaking changes. Deprecate the old version with a timeline. 4. **Version the API, not individual endpoints** — All endpoints in a version group share the same version number.
// 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;
});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();options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
// Client sends: X-Api-Version: 2.0var v1 = app.NewApiVersionSet()
.HasDeprecatedApiVersion(new ApiVersion(1, 0))
.HasApiVersion(new ApiVersion(2, 0))
.Build();
// Response headers will include: api-deprecated-versions: 1.0public 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();
}
}// 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();// 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
| 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 |
Architecture conformance check: verifies an existing codebase against its declared…
Architecture selection advisor for .NET applications. Asks structured questions about domain…
.NET Aspire for cloud-native orchestration. Covers AppHost configuration, service defaults,…
Authentication and authorization for ASP.NET Core. Covers JWT bearer tokens, OpenID Connect,…
Autonomous iteration loops for .NET: drive a broken build or failing test suite to green with…
Caching strategies for .NET 10 applications. Covers HybridCache (the default), output…