API Versioning

15 questions found

Why is API versioning important for a production web API?

Beginner
API versioning lets you evolve an API's contract (adding fields, changing behavior, removing deprecated endpoints) without breaking existing clients that depend on the current behavior. Without versioning, any breaking change forces every consumer to update simultaneously, which is impractical for public APIs or APIs with many independent client teams.
// v1: returns { name: string }
// v2: returns { firstName: string, lastName: string }
// Both versions coexist so old clients keep working during a migration window
Real-world example A payments API maintains v1 and v2 simultaneously for six months after a breaking schema change, giving integrator partners time to migrate before v1 is eventually deprecated and removed.

Common follow-ups: What's the difference between a breaking and non-breaking API change?;How long should old versions typically be supported?

RESTful Web APIs & Controllers;CI/CD Publishing & Deployment

What are the main strategies for versioning an ASP.NET Core API (URL, query string, header, media type)?

Intermediate
URL segment versioning (/api/v1/products) is the most visible and cache-friendly approach. Query string versioning (/api/products?api-version=1.0) keeps URLs stable but is less discoverable. Header versioning (a custom header like X-Api-Version) keeps URLs completely clean but is less visible in browsers/docs. Media type versioning (Accept: application/json;v=1.0) follows REST/HATEOAS purism but is the least commonly adopted in practice.
// URL segment (most common)
[Route("api/v{version:apiVersion}/products")]

// Query string
// GET /api/products?api-version=1.0

// Header
// GET /api/products  Header: X-Api-Version: 1.0
Real-world example A public-facing API chooses URL segment versioning (/api/v2/orders) specifically because it's the most discoverable and cache-proxy-friendly option for third-party integrators reading documentation.

Common follow-ups: Which strategy works best with CDN and reverse-proxy caching?;How do you combine multiple versioning strategies simultaneously?

RESTful Web APIs & Controllers;ASP.NET Core Middleware & Request Pipeline

How do you implement URL-based API versioning using the Asp.Versioning.Mvc package in ASP.NET Core?

Intermediate
The Asp.Versioning packages (successor to Microsoft.AspNetCore.Mvc.Versioning) let you register API versioning services, define supported versions per controller/action via [ApiVersion] attributes, and use a {version:apiVersion} route template placeholder so the framework automatically routes requests to the correct versioned controller or action.
builder.Services.AddApiVersioning(options => {
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
});

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ControllerBase { }

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ControllerBase { }
Real-world example An e-commerce API adds a ProductsV2Controller alongside the existing V1 controller when the product schema needs a breaking change, letting both versions run simultaneously in production.

Common follow-ups: How do you deprecate a version and communicate it via response headers?;How does versioning interact with Swagger/OpenAPI documentation generation?

ASP.NET Core Middleware & Request Pipeline;RESTful Web APIs & Controllers

How can you version a single controller action to support multiple API versions without duplicating the entire controller class?

Advanced
Instead of separate controller classes per version, you can apply multiple [ApiVersion] attributes to one controller and use [MapToApiVersion("x.0")] on individual actions to indicate which specific version(s) an action supports, letting you share common actions across versions while only duplicating the ones that actually changed.
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsController : ControllerBase {
    [HttpGet]
    public IActionResult GetAll() => Ok(_products);  // shared across both versions

    [HttpGet("{id}")]
    [MapToApiVersion("2.0")]
    public IActionResult GetByIdV2(int id) => Ok(_v2Format);  // only in v2
}
Real-world example An API adds a new filtering capability only in v2's GetAll action while keeping the v1 GetById action completely shared, minimizing duplicated code across versions within one controller.

Common follow-ups: What happens if a request doesn't match any MapToApiVersion action?;How does this compare to maintaining fully separate controllers per version?

RESTful Web APIs & Controllers;ASP.NET Core Middleware & Request Pipeline

How do you communicate API version deprecation to clients using response headers?

Intermediate
The Asp.Versioning library can automatically add an `api-supported-versions` header (listing currently supported versions) and an `api-deprecated-versions` header (listing versions still functional but scheduled for removal) to every response, giving clients a machine-readable way to detect upcoming breaking changes proactively rather than being surprised when a version is finally removed.
builder.Services.AddApiVersioning(options => {
    options.ReportApiVersions = true;  // adds api-supported-versions / api-deprecated-versions headers
});

[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase { }
Real-world example A partner integration team writes automated monitoring that checks the api-deprecated-versions header on every API response, automatically filing a ticket when their integration is still using a version flagged as deprecated.

Common follow-ups: How far in advance should deprecation typically be announced?;What HTTP status code should a fully removed version return?

RESTful Web APIs & Controllers;Diagnostics & Performance

What's the difference between versioning a REST API's contract versus versioning its underlying data model, and why does that distinction matter?

Advanced
API contract versioning concerns the shape of requests/responses exposed to clients (JSON structure, field names, endpoint paths), while the underlying data model (database schema, internal domain objects) can evolve independently as long as a translation/mapping layer (like DTOs) adapts between the internal model and each exposed API version's contract -- letting you refactor internals freely without forcing a new API version for every internal change.
// Internal domain model can change freely
public class ProductEntity { public decimal Price { get; set; } public string Currency { get; set; } }

// v1 DTO stays stable regardless of internal changes
public class ProductV1Dto { public decimal Price { get; set; } }  // USD assumed

// v2 DTO exposes the new capability
public class ProductV2Dto { public decimal Amount { get; set; } public string Currency { get; set; } }
Real-world example A team refactors their internal Product entity extensively to support multi-currency pricing without bumping the API version at all, since a mapping layer keeps translating to the unchanged v1 DTO shape for existing clients.

Common follow-ups: How does this relate to the Anti-Corruption Layer pattern?;What's the maintenance cost of keeping many DTO versions in sync?

Entity Framework Core & Data Access;RESTful Web APIs & Controllers

How does semantic versioning (SemVer) inform decisions about when to bump an API's major, minor, or patch version?

Intermediate
Semantic versioning conventions (MAJOR.MINOR.PATCH) map naturally to API changes: a MAJOR bump signals breaking changes (removed fields, changed response shapes, incompatible behavior), a MINOR bump signals backward-compatible additions (new optional fields, new endpoints), and a PATCH bump signals backward-compatible bug fixes with no contract change. Most public API versioning schemes (like /api/v1, /api/v2) only expose the MAJOR component externally, treating MINOR/PATCH as internal implementation detail that doesn't require client action.
// Externally exposed: only major version
// /api/v1/products  /api/v2/products

// Internally tracked via SemVer in release notes:
// v1.3.2 -- patch fix, no client action needed
// v1.4.0 -- new optional field added, backward compatible
// v2.0.0 -- breaking change, new URL segment required
Real-world example An API's changelog documents every release using full SemVer internally (v1.4.2, v1.5.0) while only the major version (v1, v2) is ever reflected in the actual URL path clients call, keeping the versioning surface simple for consumers.

Common follow-ups: Should minor/patch versions ever be exposed in the URL?;How does this map to versioning conventions in package managers like NuGet?

RESTful Web APIs & Controllers;CI/CD Publishing & Deployment

What is the 'tolerant reader' pattern, and how does it reduce the need for frequent API versioning?

Advanced
The tolerant reader pattern designs API clients to ignore unknown fields and gracefully handle missing optional fields, rather than strictly validating against an exact expected schema. When both server and client follow this principle, many additive, backward-compatible changes (new optional fields, extra response properties) don't require a version bump at all, since well-behaved clients simply ignore what they don't understand.
// Server adds a new optional field without a version bump:
{ "id": 1, "name": "Widget", "weightKg": 2.5 }  // new field

// Tolerant client (e.g., using JsonSerializerOptions with unmapped members ignored)
var options = new JsonSerializerOptions {
    UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip
};
Real-world example A mobile app built with a tolerant JSON deserializer continues working seamlessly when the backend team adds new optional fields to API responses, avoiding an app-store release cycle just to accommodate additive backend changes.

Common follow-ups: What are the risks of being too tolerant (silently ignoring meaningful changes)?;How does this principle relate to Postel's Law?

RESTful Web APIs & Controllers;Global Exception Handling & Middleware

How do you version an API's OpenAPI/Swagger documentation to show separate docs per version?

Intermediate
Using Swashbuckle or NSwag together with the versioning library's IApiVersionDescriptionProvider, you can generate a separate OpenAPI document per registered API version, configuring Swagger UI to expose a dropdown letting consumers switch between documented versions (e.g., /swagger/v1/swagger.json and /swagger/v2/swagger.json).
builder.Services.AddSwaggerGen();
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();

app.UseSwaggerUI(options => {
    foreach (var description in provider.ApiVersionDescriptions)
        options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
});
Real-world example An API's public developer portal shows a version dropdown in Swagger UI, letting third-party integrators explore both the current v2 and the still-supported legacy v1 documentation side by side.

Common follow-ups: How does ConfigureSwaggerOptions discover registered API versions automatically?;What happens to Swagger docs when a version is removed?

ASP.NET Core Middleware & Request Pipeline;RESTful Web APIs & Controllers

What are the trade-offs of versioning an entire API at once (global versioning) versus versioning individual endpoints independently?

Advanced
Global versioning (bump the whole API to v2 even if only one endpoint changed) is simpler to reason about and communicate but forces clients to migrate everything at once, even unaffected endpoints. Independent endpoint versioning (only the changed endpoint gets a new version while others stay stable) minimizes unnecessary client churn but increases complexity in routing, documentation, and mental overhead of tracking which specific endpoints are at which version.
// Global versioning: entire API bumps together
// /api/v2/products  /api/v2/orders  /api/v2/customers (all v2, even if only products changed)

// Independent versioning: only the changed resource bumps
// /api/v1/orders  /api/v2/products  /api/v1/customers (mixed versions)
Real-world example A large platform API with dozens of resource types adopts independent endpoint versioning specifically to avoid forcing every integrator to re-test their entire integration whenever any single unrelated endpoint changes.

Common follow-ups: Which large public APIs use each approach (e.g., Stripe vs GitHub)?;How does independent versioning complicate client SDK generation?

RESTful Web APIs & Controllers;Microservices & Distributed Architecture Patterns

Showing 1–10 of 15