[INT-011] int: Add API versioning strategy

This commit is contained in:
senke 2025-12-25 15:25:33 +01:00
parent 74f9531c50
commit 0bd12aa91d
3 changed files with 545 additions and 5 deletions

502
API_VERSIONING_STRATEGY.md Normal file
View file

@ -0,0 +1,502 @@
# 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
1. **Active**: Current recommended version, fully supported
2. **Deprecated**: Still functional but will be removed in the future
3. **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
```json
// 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
```json
// 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:
1. You need to make breaking changes
2. You want to introduce significant architectural changes
3. You need to deprecate old patterns
4. You want to clean up technical debt
### Process for Creating v2 (Example)
#### Step 1: Planning (3-6 months before release)
1. **Identify breaking changes**
- List all breaking changes
- Document impact on consumers
- Create migration guide
2. **Design new version**
- Design new endpoints
- Design new request/response formats
- Plan backward compatibility strategy
3. **Announce to consumers**
- Send deprecation notice for v1
- Announce v2 release date
- Provide migration timeline
#### Step 2: Implementation
1. **Create v2 handlers**
```go
// New v2 handler
func (h *TrackHandler) GetTrackV2(c *gin.Context) {
// v2 implementation
}
```
2. **Register v2 version**
```go
vm.RegisterVersion(&APIVersion{
Version: "v2",
Deprecated: false,
Description: "New API version with improved endpoints",
})
```
3. **Add v2 routes**
```go
v2 := router.Group("/api/v2")
v2.GET("/tracks/:id", trackHandler.GetTrackV2)
```
4. **Update tests**
- Add tests for v2 endpoints
- Test backward compatibility
- Test migration scenarios
#### Step 3: Release
1. **Deploy v2**
- Deploy to staging
- Test thoroughly
- Deploy to production
2. **Monitor usage**
- Track v1 vs v2 usage
- Monitor errors
- Collect feedback
3. **Update documentation**
- Update Swagger/OpenAPI docs
- Update migration guides
- Update examples
#### Step 4: Deprecation (6-12 months after v2 release)
1. **Mark v1 as deprecated**
```go
vm.RegisterVersion(&APIVersion{
Version: "v1",
Deprecated: true,
SunsetDate: "2026-12-31T00:00:00Z",
Description: "Deprecated - migrate to v2",
})
```
2. **Add deprecation headers**
- `X-API-Version-Deprecated: true`
- `Sunset: 2026-12-31T00:00:00Z`
3. **Notify consumers**
- Send deprecation notices
- Provide migration timeline
- Offer support for migration
#### Step 5: End of Life (12+ months after deprecation)
1. **Remove v1 support**
- Remove v1 routes
- Remove v1 handlers
- Update documentation
2. **Final notification**
- Send final notice
- Provide last migration window
- Close v1 support tickets
## Guidelines for API Developers
### When Adding New Features
1. **Check if it's breaking**
- If breaking → new major version
- If non-breaking → same version
2. **Document changes**
- Update Swagger annotations
- Update API documentation
- Add migration notes if needed
3. **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
1. **Test both versions**
```go
func TestTrackEndpoint_V1(t *testing.T) {
// Test v1 endpoint
}
func TestTrackEndpoint_V2(t *testing.T) {
// Test v2 endpoint
}
```
2. **Test backward compatibility**
- Ensure v1 still works
- Test migration scenarios
- Test deprecation warnings
## Guidelines for API Consumers
### Best Practices
1. **Always specify version explicitly**
```bash
# Good
GET /api/v1/tracks
# Also good
GET /api/tracks
X-API-Version: v1
```
2. **Monitor deprecation notices**
- Check `/api/versions` endpoint
- Monitor response headers
- Subscribe to API updates
3. **Test new versions early**
- Test in staging
- Migrate gradually
- Don't wait until EOL
4. **Handle version errors gracefully**
```javascript
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:
```json
{
"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 used
- `X-API-Version-Deprecated`: `true` if version is deprecated
- `Sunset`: RFC3339 date when version will be removed (if deprecated)
## Examples
### Using URL Path Versioning
```bash
# v1 endpoint
curl https://api.veza.app/api/v1/tracks
# v2 endpoint
curl https://api.veza.app/api/v2/tracks
```
### Using Header Versioning
```bash
# 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
```bash
# 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
```bash
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](https://restfulapi.net/versioning/)
- [API Versioning Strategies](https://www.baeldung.com/rest-versioning)
- [Semantic Versioning](https://semver.org/)
- [RFC 7231 - HTTP/1.1 Semantics](https://tools.ietf.org/html/rfc7231)
## 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 details
- `OPENAPI_MAINTENANCE_GUIDE.md` - API documentation maintenance

View file

@ -10489,8 +10489,11 @@
"description": "Implement API versioning for future compatibility",
"owner": "fullstack",
"estimated_hours": 4,
"status": "todo",
"files_involved": [],
"status": "completed",
"files_involved": [
"API_VERSIONING_STRATEGY.md",
"veza-backend-api/docs/API_VERSIONING.md"
],
"implementation_steps": [
{
"step": 1,
@ -10510,7 +10513,8 @@
"Unit tests",
"Integration tests"
],
"notes": ""
"notes": "Added comprehensive API versioning strategy:\n- Created API_VERSIONING_STRATEGY.md with complete versioning strategy\n- Documented versioning methods (URL path, headers)\n- Defined breaking vs non-breaking changes\n- Created version lifecycle process\n- Added guidelines for API developers and consumers\n- Documented migration process\n- Added decision matrix for change types\n- Updated API_VERSIONING.md with implementation details\n- Versioning infrastructure already implemented (VersionManager, middleware)\n- Tests pass and validate versioning functionality",
"completed_at": "2025-12-25T14:25:31.521191Z"
},
{
"id": "INT-012",

View file

@ -113,12 +113,46 @@ GET /api/v2/tracks
POST /api/v2/playlists
```
## Implementation Details
### Version Manager
The API uses a `VersionManager` to handle version registration and validation:
```go
vm := NewVersionManager(logger)
vm.RegisterVersion(&APIVersion{
Version: "v1",
Deprecated: false,
Description: "Current stable API version",
})
```
### Version Middleware
The `VersionMiddleware` extracts the version from:
1. `X-API-Version` header
2. `Accept` header (application/vnd.veza.v1+json)
3. URL path (/api/v1/...)
### Version Context
The version is stored in the Gin context and can be accessed:
```go
version := GetAPIVersion(c) // Returns "v1", "v2", etc.
versionInfo := GetAPIVersionInfo(c) // Returns full APIVersion struct
```
## References
- [REST API Versioning Best Practices](https://restfulapi.net/versioning/)
- [API Versioning Strategies](https://www.baeldung.com/rest-versioning)
- See `API_VERSIONING_STRATEGY.md` for comprehensive strategy document
---
**Last Updated**: 2025-01-27
**Maintained By**: Veza Backend Team
**Last Updated**: 2025-12-25
**Maintained By**: Veza Backend Team
**Related Documents**:
- `API_VERSIONING_STRATEGY.md` - Comprehensive versioning strategy