CodeOath
← All posts
.NET Core / Web API70 min total · 19 parts

Building REST APIs with ASP.NET Core: Routing, Middleware, and Dependency Injection

Contents — Part 16 of 19: API Versioning and Documentation
Part 16 of 19 · ~1 min

API Versioning and Documentation

Real APIs change over time without every consumer upgrading in lockstep, so versioning is worth planning for from early on rather than retrofitting later:

[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id}"), MapToApiVersion("1.0")]
    public IActionResult GetByIdV1(int id) => Ok(/* legacy shape */);

    [HttpGet("{id}"), MapToApiVersion("2.0")]
    public IActionResult GetByIdV2(int id) => Ok(/* current shape */);
}

URL-segment versioning (api/v1/orders) is the most discoverable approach for API consumers; header-based versioning is also common and keeps URLs stable across versions at the cost of being less obvious when browsing the API directly.

For documentation, OpenAPI/Swagger generation reads your controllers' attributes and types to produce an interactive spec automatically:

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(); // interactive docs, typically served at /swagger
}

Response type attributes make the generated documentation (and generated client SDKs) accurate about what each action can actually return, beyond what the compiler alone can infer from ActionResult<T>:

[HttpGet("{id}")]
[ProducesResponseType(typeof(Order), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Order>> GetById(int id) { /* ... */ }