12 KiB
API Versioning Strategy
INT-011: Add API versioning strategy
Date: 2025-12-25
Status: Completed
Overview
This document defines the comprehensive API versioning strategy for Veza Backend API. It provides guidelines for both API developers and consumers on how to handle API versions, breaking changes, and migrations.
Current Version: v1
Status: Active
Implementation: URL Path Versioning (primary), Header-based (supported)
Versioning Methods
The Veza API supports multiple methods for specifying the API version, in order of precedence:
1. URL Path Versioning (Primary)
The version is included in the URL path:
GET /api/v1/tracks
POST /api/v1/playlists
GET /api/v2/users/me
Advantages:
- Clear and explicit
- Easy to understand
- RESTful
- Cache-friendly
Format: /api/v{major}/...
2. Header-Based Versioning (Supported)
Clients can specify the version using HTTP headers:
X-API-Version Header
X-API-Version: v1
Accept Header (Content Negotiation)
Accept: application/vnd.veza.v1+json
Advantages:
- Clean URLs
- Flexible for clients
- Supports content negotiation
Note: When using header-based versioning, the URL path should still include /api/ but the version can be omitted or different.
Version Format
- Format:
v{major} - Examples:
v1,v2,v3 - Major versions indicate breaking changes
- No minor/patch versions in the API path (semantic versioning is used for the application itself)
Version Lifecycle
Current Version: v1
- Status: Active
- Released: 2025-01-01
- Deprecation Date: TBD
- End of Life: TBD
- Breaking Changes: None planned
- Description: Current stable API version
Version States
- Active: Current recommended version, fully supported
- Deprecated: Still functional but will be removed in the future
- End of Life: No longer supported, requests will fail
Version Lifecycle Timeline
┌─────────────┐
│ Active │ ← Current version, fully supported
└──────┬──────┘
│
│ (6+ months notice)
▼
┌─────────────┐
│ Deprecated │ ← Still works, but marked for removal
└──────┬──────┘
│
│ (12+ months support)
▼
┌─────────────┐
│ End of Life │ ← Removed, requests fail
└─────────────┘
Breaking vs Non-Breaking Changes
Breaking Changes (Require New Major Version)
Breaking changes require a new major version (e.g., v1 → v2):
Request Changes
- ✅ Removed endpoints
- ✅ Changed HTTP methods (GET → POST)
- ✅ Removed required fields in request body
- ✅ Changed field types (string → integer)
- ✅ Changed field names (camelCase → snake_case)
- ✅ Changed validation rules (making optional fields required)
- ✅ Changed authentication mechanisms
Response Changes
- ✅ Changed response structure (removing fields)
- ✅ Changed field types in responses
- ✅ Changed error response format
- ✅ Removed response fields
- ✅ Changed HTTP status codes for same endpoint
Examples of Breaking Changes
// v1 Response
{
"id": "123",
"name": "Track Name"
}
// v2 Response (BREAKING - removed 'name', added 'title')
{
"id": "123",
"title": "Track Name"
}
Non-Breaking Changes (Same Version)
These changes can be made within the same version:
Request Changes
- ✅ New optional fields in request body
- ✅ New query parameters
- ✅ New endpoints
- ✅ Relaxed validation (making required fields optional)
Response Changes
- ✅ New optional fields in responses
- ✅ New endpoints
- ✅ New error codes (additional error types)
- ✅ Performance improvements
- ✅ Bug fixes (fixing incorrect behavior)
Examples of Non-Breaking Changes
// v1 Response
{
"id": "123",
"name": "Track Name"
}
// v1 Response (NON-BREAKING - added optional field)
{
"id": "123",
"name": "Track Name",
"description": "Optional description" // New field
}
Creating a New API Version
When to Create a New Version
Create a new major version when:
- You need to make breaking changes
- You want to introduce significant architectural changes
- You need to deprecate old patterns
- You want to clean up technical debt
Process for Creating v2 (Example)
Step 1: Planning (3-6 months before release)
-
Identify breaking changes
- List all breaking changes
- Document impact on consumers
- Create migration guide
-
Design new version
- Design new endpoints
- Design new request/response formats
- Plan backward compatibility strategy
-
Announce to consumers
- Send deprecation notice for v1
- Announce v2 release date
- Provide migration timeline
Step 2: Implementation
-
Create v2 handlers
// New v2 handler func (h *TrackHandler) GetTrackV2(c *gin.Context) { // v2 implementation } -
Register v2 version
vm.RegisterVersion(&APIVersion{ Version: "v2", Deprecated: false, Description: "New API version with improved endpoints", }) -
Add v2 routes
v2 := router.Group("/api/v2") v2.GET("/tracks/:id", trackHandler.GetTrackV2) -
Update tests
- Add tests for v2 endpoints
- Test backward compatibility
- Test migration scenarios
Step 3: Release
-
Deploy v2
- Deploy to staging
- Test thoroughly
- Deploy to production
-
Monitor usage
- Track v1 vs v2 usage
- Monitor errors
- Collect feedback
-
Update documentation
- Update Swagger/OpenAPI docs
- Update migration guides
- Update examples
Step 4: Deprecation (6-12 months after v2 release)
-
Mark v1 as deprecated
vm.RegisterVersion(&APIVersion{ Version: "v1", Deprecated: true, SunsetDate: "2026-12-31T00:00:00Z", Description: "Deprecated - migrate to v2", }) -
Add deprecation headers
X-API-Version-Deprecated: trueSunset: 2026-12-31T00:00:00Z
-
Notify consumers
- Send deprecation notices
- Provide migration timeline
- Offer support for migration
Step 5: End of Life (12+ months after deprecation)
-
Remove v1 support
- Remove v1 routes
- Remove v1 handlers
- Update documentation
-
Final notification
- Send final notice
- Provide last migration window
- Close v1 support tickets
Guidelines for API Developers
When Adding New Features
-
Check if it's breaking
- If breaking → new major version
- If non-breaking → same version
-
Document changes
- Update Swagger annotations
- Update API documentation
- Add migration notes if needed
-
Maintain backward compatibility
- During deprecation period
- Test both versions
- Don't break existing consumers
Code Organization
internal/
├── handlers/
│ ├── track_handler.go # v1 handlers
│ └── track_handler_v2.go # v2 handlers (when needed)
├── api/
│ ├── router.go # Route setup
│ └── versioning.go # Version management
Testing Strategy
-
Test both versions
func TestTrackEndpoint_V1(t *testing.T) { // Test v1 endpoint } func TestTrackEndpoint_V2(t *testing.T) { // Test v2 endpoint } -
Test backward compatibility
- Ensure v1 still works
- Test migration scenarios
- Test deprecation warnings
Guidelines for API Consumers
Best Practices
-
Always specify version explicitly
# Good GET /api/v1/tracks # Also good GET /api/tracks X-API-Version: v1 -
Monitor deprecation notices
- Check
/api/versionsendpoint - Monitor response headers
- Subscribe to API updates
- Check
-
Test new versions early
- Test in staging
- Migrate gradually
- Don't wait until EOL
-
Handle version errors gracefully
if (response.status === 400 && response.body.error === "Unsupported API version") { // Handle version error console.log("Available versions:", response.body.available_versions); }
Migration Checklist
When migrating from v1 to v2:
- Review breaking changes documentation
- Test v2 endpoints in staging
- Update API client code
- Update request/response handling
- Test all affected features
- Update error handling
- Deploy to production
- Monitor for issues
- Remove v1 code after successful migration
Version Information Endpoint
GET /api/versions
Returns information about all available API versions:
{
"current_version": "v1",
"versions": {
"v1": {
"version": "v1",
"deprecated": false,
"description": "Current stable API version"
},
"v2": {
"version": "v2",
"deprecated": false,
"description": "New API version",
"sunset_date": null
}
}
}
Response Headers
The API includes version information in response headers:
X-API-Version: Current version being usedX-API-Version-Deprecated:trueif version is deprecatedSunset: RFC3339 date when version will be removed (if deprecated)
Examples
Using URL Path Versioning
# v1 endpoint
curl https://api.veza.app/api/v1/tracks
# v2 endpoint
curl https://api.veza.app/api/v2/tracks
Using Header Versioning
# Using X-API-Version header
curl -H "X-API-Version: v1" https://api.veza.app/api/tracks
# Using Accept header
curl -H "Accept: application/vnd.veza.v1+json" https://api.veza.app/api/tracks
Handling Deprecated Versions
# Response includes deprecation headers
curl -H "X-API-Version: v1" https://api.veza.app/api/tracks
# Response headers:
# X-API-Version: v1
# X-API-Version-Deprecated: true
# Sunset: 2026-12-31T00:00:00Z
Decision Matrix
| Change Type | Action | Version Impact |
|---|---|---|
| New endpoint | Add to current version | Same version |
| New optional field | Add to current version | Same version |
| Remove endpoint | Create new version | New major version |
| Change field type | Create new version | New major version |
| Change field name | Create new version | New major version |
| Remove required field | Create new version | New major version |
| Add required field | Create new version | New major version |
| Change error format | Create new version | New major version |
| Performance improvement | Add to current version | Same version |
| Bug fix | Add to current version | Same version |
Testing
Running Version Tests
cd veza-backend-api
go test ./internal/api/... -v
Test Coverage
- ✅ Version manager functionality
- ✅ Version middleware
- ✅ Header-based versioning
- ✅ URL path versioning
- ✅ Deprecated version handling
- ✅ Version info endpoint
References
- REST API Versioning Best Practices
- API Versioning Strategies
- Semantic Versioning
- RFC 7231 - HTTP/1.1 Semantics
Maintenance
This strategy should be reviewed and updated:
- When creating a new major version
- When deprecating a version
- When changing versioning approach
- Annually for best practices updates
Last Updated: 2025-12-25
Maintained By: Veza Backend Team
Related Documents:
veza-backend-api/docs/API_VERSIONING.md- Technical implementation detailsOPENAPI_MAINTENANCE_GUIDE.md- API documentation maintenance