API Versioning Strategies
Design APIs that evolve while maintaining backward compatibility.
By EMEPublished: February 20, 2025
api versioningcompatibilityevolutionbackward compatibility
A Simple Analogy
API versioning is like a company's product line. As products improve, you maintain older versions for existing customers while offering newer versions. You can't force everyone to upgrade simultaneously.
Why Version APIs?
- Evolution: APIs must change to add features
- Compatibility: Existing clients shouldn't break
- Gradual migration: Time for clients to upgrade
- Support: Maintain old versions for existing users
- Flexibility: Different clients use different versions
Versioning Strategies
URL Path Versioning
GET /api/v1/users # Version 1
GET /api/v2/users # Version 2
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers() => Ok(new[] { "v1" });
}
Query Parameter Versioning
GET /api/users?version=1
GET /api/users?version=2
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers([FromQuery] int version = 1)
{
return version switch
{
1 => Ok(new[] { "name", "email" }),
2 => Ok(new[] { "id", "name", "email", "phone" }),
_ => BadRequest("Unsupported version")
};
}
}
Header Versioning
GET /api/users
X-API-Version: 1
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers([FromHeader(Name = "X-API-Version")] int version = 1)
{
return version switch
{
1 => Ok(GetUsersV1()),
2 => Ok(GetUsersV2()),
_ => BadRequest("Unsupported version")
};
}
}
Media Type Versioning
GET /api/users
Accept: application/vnd.company.user-v1+json
Deprecation Strategy
[Obsolete("Use v2 instead")]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersControllerV1 : ControllerBase
{
[HttpGet]
public IActionResult GetUsers()
{
return Ok(new[] { new { Name = "Alice" } });
}
}
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersControllerV2 : ControllerBase
{
[HttpGet]
public IActionResult GetUsers()
{
return Ok(new[] {
new { Id = 1, Name = "Alice", Email = "alice@example.com" }
});
}
}
Practical Example
// V1: Simple user response
public class UserDtoV1
{
public string Name { get; set; }
public string Email { get; set; }
}
// V2: Extended with additional fields
public class UserDtoV2
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
public string Status { get; set; }
}
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
[MapToApiVersion("1.0")]
public async Task<ActionResult<UserDtoV1>> GetUserV1(int id)
{
var user = await _userService.GetUserAsync(id);
return Ok(new UserDtoV1
{
Name = user.Name,
Email = user.Email
});
}
[HttpGet("{id}")]
[MapToApiVersion("2.0")]
public async Task<ActionResult<UserDtoV2>> GetUserV2(int id)
{
var user = await _userService.GetUserAsync(id);
return Ok(new UserDtoV2
{
Id = user.Id,
Name = user.Name,
Email = user.Email,
CreatedAt = user.CreatedAt,
Status = user.Status
});
}
}
Sunset Policy
Version 1: Active until Dec 31, 2024
Version 2: Supported for 2 years after release
Version 3: Current stable version
Timeline:
- V1: Deprecated (Oct 2024), Sunset (Dec 31, 2024)
- V2: Stable, 18 months remaining
- V3: Current, fully supported
Best Practices
- Plan ahead: Design versioning from day one
- Semantic versioning: Major.Minor.Patch
- Deprecation warnings: Notify clients early
- Sunset timeline: Clear migration path
- Documentation: Document all versions
Related Concepts to Explore
- API gateway versioning
- Content negotiation
- Breaking vs. non-breaking changes
- Schema evolution
- Backward compatibility testing
Summary
API versioning enables controlled evolution while maintaining backward compatibility. Choose a strategy, communicate clearly, and provide migration paths for smooth client upgrades.