[INT-011] int: Add API versioning strategy
This commit is contained in:
parent
5bdf3abbb4
commit
28e5c62968
3 changed files with 545 additions and 5 deletions
502
API_VERSIONING_STRATEGY.md
Normal file
502
API_VERSIONING_STRATEGY.md
Normal 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
|
||||
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue