392 lines
8.1 KiB
Markdown
392 lines
8.1 KiB
Markdown
|
|
# API Versioning Strategies
|
||
|
|
|
||
|
|
## Why Version APIs?
|
||
|
|
|
||
|
|
API versioning allows you to evolve your API while maintaining backward compatibility for existing clients. Breaking changes require a new version.
|
||
|
|
|
||
|
|
### Breaking Changes
|
||
|
|
|
||
|
|
Changes that require a new version:
|
||
|
|
- Removing or renaming fields
|
||
|
|
- Changing field types (string to integer)
|
||
|
|
- Adding required fields to requests
|
||
|
|
- Changing response structure
|
||
|
|
- Removing endpoints
|
||
|
|
- Changing HTTP status codes for same scenario
|
||
|
|
- Changing authentication mechanisms
|
||
|
|
|
||
|
|
### Non-Breaking Changes
|
||
|
|
|
||
|
|
Safe changes that don't require a new version:
|
||
|
|
- Adding new endpoints
|
||
|
|
- Adding optional request fields
|
||
|
|
- Adding new fields to responses (clients should ignore unknown fields)
|
||
|
|
- Fixing bugs
|
||
|
|
- Performance improvements
|
||
|
|
- Adding new HTTP methods to existing resources
|
||
|
|
|
||
|
|
## Versioning Strategies
|
||
|
|
|
||
|
|
### 1. URI Versioning
|
||
|
|
|
||
|
|
Most common and visible approach. Version is part of the URL path.
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /v1/users/123
|
||
|
|
GET /v2/users/123
|
||
|
|
```
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- Clear and visible in URLs
|
||
|
|
- Easy to understand and implement
|
||
|
|
- Simple routing and caching
|
||
|
|
- Can run multiple versions simultaneously
|
||
|
|
|
||
|
|
**Disadvantages:**
|
||
|
|
- Violates REST principle (same resource, different URIs)
|
||
|
|
- Requires updating client code to change version
|
||
|
|
- Can lead to URI proliferation
|
||
|
|
|
||
|
|
**Implementation:**
|
||
|
|
```
|
||
|
|
/v1/users
|
||
|
|
/v1/products
|
||
|
|
/v2/users # New version with breaking changes
|
||
|
|
/v2/products
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Header Versioning
|
||
|
|
|
||
|
|
Version specified in HTTP headers (Accept header or custom header).
|
||
|
|
|
||
|
|
**Accept Header:**
|
||
|
|
```http
|
||
|
|
GET /users/123
|
||
|
|
Accept: application/vnd.myapi.v1+json
|
||
|
|
|
||
|
|
GET /users/123
|
||
|
|
Accept: application/vnd.myapi.v2+json
|
||
|
|
```
|
||
|
|
|
||
|
|
**Custom Header:**
|
||
|
|
```http
|
||
|
|
GET /users/123
|
||
|
|
API-Version: 1
|
||
|
|
|
||
|
|
GET /users/123
|
||
|
|
API-Version: 2
|
||
|
|
```
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- URIs remain stable
|
||
|
|
- More RESTful (same resource, same URI)
|
||
|
|
- Separates versioning from resource identification
|
||
|
|
|
||
|
|
**Disadvantages:**
|
||
|
|
- Less visible (harder to debug)
|
||
|
|
- More complex routing
|
||
|
|
- Difficult to test in browser
|
||
|
|
- Cache complexity
|
||
|
|
|
||
|
|
### 3. Query Parameter Versioning
|
||
|
|
|
||
|
|
Version specified as query parameter.
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /users/123?version=1
|
||
|
|
GET /users/123?version=2
|
||
|
|
|
||
|
|
# or
|
||
|
|
GET /users/123?api-version=1
|
||
|
|
GET /users/123?api-version=2
|
||
|
|
```
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- Simple to implement
|
||
|
|
- Easy to test
|
||
|
|
- Visible in URLs
|
||
|
|
|
||
|
|
**Disadvantages:**
|
||
|
|
- Pollutes query string
|
||
|
|
- Not semantic (version not a filter)
|
||
|
|
- Can interfere with other query params
|
||
|
|
|
||
|
|
### 4. Content Negotiation
|
||
|
|
|
||
|
|
Client specifies desired version through content negotiation.
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /users/123
|
||
|
|
Accept: application/vnd.myapi+json; version=1
|
||
|
|
|
||
|
|
GET /users/123
|
||
|
|
Accept: application/vnd.myapi+json; version=2
|
||
|
|
```
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- Very RESTful
|
||
|
|
- Flexible content type negotiation
|
||
|
|
- Stable URIs
|
||
|
|
|
||
|
|
**Disadvantages:**
|
||
|
|
- Complex implementation
|
||
|
|
- Less intuitive for developers
|
||
|
|
- Harder to test
|
||
|
|
|
||
|
|
## Recommended Approach
|
||
|
|
|
||
|
|
**URI versioning is recommended for most APIs** because:
|
||
|
|
- It's the most explicit and discoverable
|
||
|
|
- Easy to understand and debug
|
||
|
|
- Simple to implement and maintain
|
||
|
|
- Clear separation between versions
|
||
|
|
|
||
|
|
```
|
||
|
|
/v1/users
|
||
|
|
/v2/users
|
||
|
|
/v3/users
|
||
|
|
```
|
||
|
|
|
||
|
|
## Version Format
|
||
|
|
|
||
|
|
### Major Versions Only
|
||
|
|
|
||
|
|
Use simple major versions (v1, v2, v3) for public APIs:
|
||
|
|
```
|
||
|
|
/v1/users
|
||
|
|
/v2/users
|
||
|
|
```
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- Simple and clear
|
||
|
|
- Easy to communicate
|
||
|
|
- Forces thoughtful breaking changes
|
||
|
|
|
||
|
|
### Date-Based Versions
|
||
|
|
|
||
|
|
Some APIs use dates for versions:
|
||
|
|
```
|
||
|
|
/2024-01-01/users
|
||
|
|
/2024-06-15/users
|
||
|
|
```
|
||
|
|
|
||
|
|
**Used by:** Stripe, GitHub API
|
||
|
|
|
||
|
|
**Advantages:**
|
||
|
|
- Clear when version was released
|
||
|
|
- Easy to understand timeline
|
||
|
|
- No confusion about major/minor
|
||
|
|
|
||
|
|
**Disadvantages:**
|
||
|
|
- Less intuitive for clients
|
||
|
|
- Harder to understand what changed
|
||
|
|
|
||
|
|
## Version Lifecycle
|
||
|
|
|
||
|
|
### 1. Introduction Phase
|
||
|
|
|
||
|
|
New version is released alongside existing version:
|
||
|
|
```
|
||
|
|
/v1/users # Still supported
|
||
|
|
/v2/users # New version available
|
||
|
|
```
|
||
|
|
|
||
|
|
Announce new version:
|
||
|
|
- Blog post explaining changes
|
||
|
|
- Migration guide
|
||
|
|
- Breaking changes list
|
||
|
|
- Timeline for v1 deprecation
|
||
|
|
|
||
|
|
### 2. Deprecation Phase
|
||
|
|
|
||
|
|
Mark old version as deprecated but keep it running:
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /v1/users/123
|
||
|
|
|
||
|
|
Response:
|
||
|
|
Deprecation: true
|
||
|
|
Sunset: Wed, 15 Jan 2025 00:00:00 GMT
|
||
|
|
Link: </v2/users/123>; rel="successor-version"
|
||
|
|
|
||
|
|
{
|
||
|
|
"id": 123,
|
||
|
|
"name": "John Doe"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Deprecation Headers:**
|
||
|
|
- `Deprecation: true` - Indicates version is deprecated
|
||
|
|
- `Sunset: <date>` - When version will be removed (RFC 8594)
|
||
|
|
- `Link: <url>; rel="successor-version"` - Points to new version
|
||
|
|
|
||
|
|
### 3. Sunset Phase
|
||
|
|
|
||
|
|
Old version is shut down on announced date.
|
||
|
|
|
||
|
|
Return 410 Gone for deprecated endpoints:
|
||
|
|
```http
|
||
|
|
GET /v1/users/123
|
||
|
|
|
||
|
|
Response: 410 Gone
|
||
|
|
{
|
||
|
|
"error": {
|
||
|
|
"code": "VERSION_SUNSET",
|
||
|
|
"message": "API v1 was sunset on 2025-01-15. Please use v2.",
|
||
|
|
"documentation_url": "https://api.example.com/docs/migration-v1-to-v2"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Deprecation Policy
|
||
|
|
|
||
|
|
### Recommended Timeline
|
||
|
|
|
||
|
|
1. **Announce deprecation** - At least 6 months before sunset
|
||
|
|
2. **Support period** - Run both versions for 6-12 months
|
||
|
|
3. **Sunset date** - Clear date communicated in advance
|
||
|
|
4. **Grace period** - 30 days of 410 Gone responses before complete shutdown
|
||
|
|
|
||
|
|
### Communication Channels
|
||
|
|
|
||
|
|
- API response headers
|
||
|
|
- Email to registered developers
|
||
|
|
- Blog posts and changelog
|
||
|
|
- Dashboard notifications
|
||
|
|
- Documentation updates
|
||
|
|
- Status page announcements
|
||
|
|
|
||
|
|
## Migration Strategy
|
||
|
|
|
||
|
|
### Provide Migration Guide
|
||
|
|
|
||
|
|
```markdown
|
||
|
|
# Migrating from v1 to v2
|
||
|
|
|
||
|
|
## Breaking Changes
|
||
|
|
|
||
|
|
### User Resource Changes
|
||
|
|
|
||
|
|
**v1:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"id": 123,
|
||
|
|
"name": "John Doe",
|
||
|
|
"email": "john@example.com"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**v2:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"id": 123,
|
||
|
|
"first_name": "John",
|
||
|
|
"last_name": "Doe",
|
||
|
|
"email": "john@example.com"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration:**
|
||
|
|
- Split `name` field into `first_name` and `last_name`
|
||
|
|
- Update client code to use new fields
|
||
|
|
```
|
||
|
|
|
||
|
|
### Offer Tools
|
||
|
|
|
||
|
|
- Migration scripts
|
||
|
|
- SDK updates
|
||
|
|
- API diff viewer
|
||
|
|
- Compatibility layer (temporary)
|
||
|
|
|
||
|
|
## Version Discovery
|
||
|
|
|
||
|
|
### Root Endpoint
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /
|
||
|
|
|
||
|
|
Response:
|
||
|
|
{
|
||
|
|
"versions": {
|
||
|
|
"v1": {
|
||
|
|
"status": "deprecated",
|
||
|
|
"sunset_date": "2025-01-15",
|
||
|
|
"documentation_url": "https://api.example.com/docs/v1"
|
||
|
|
},
|
||
|
|
"v2": {
|
||
|
|
"status": "current",
|
||
|
|
"documentation_url": "https://api.example.com/docs/v2"
|
||
|
|
},
|
||
|
|
"v3": {
|
||
|
|
"status": "beta",
|
||
|
|
"documentation_url": "https://api.example.com/docs/v3"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Version Info Endpoint
|
||
|
|
|
||
|
|
```http
|
||
|
|
GET /v2/version
|
||
|
|
|
||
|
|
Response:
|
||
|
|
{
|
||
|
|
"version": "v2",
|
||
|
|
"released": "2024-01-15",
|
||
|
|
"status": "stable",
|
||
|
|
"sunset_date": null
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## OpenAPI Versioning
|
||
|
|
|
||
|
|
### Separate Specs per Version
|
||
|
|
|
||
|
|
```
|
||
|
|
openapi-v1.yaml
|
||
|
|
openapi-v2.yaml
|
||
|
|
openapi-v3.yaml
|
||
|
|
```
|
||
|
|
|
||
|
|
Each spec is complete and independent.
|
||
|
|
|
||
|
|
### Single Spec with Servers
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
openapi: 3.1.0
|
||
|
|
info:
|
||
|
|
title: My API
|
||
|
|
version: 2.0.0
|
||
|
|
servers:
|
||
|
|
- url: https://api.example.com/v1
|
||
|
|
description: Version 1 (deprecated)
|
||
|
|
- url: https://api.example.com/v2
|
||
|
|
description: Version 2 (current)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
1. **Version from day one** - Start with /v1, not /api
|
||
|
|
2. **Major versions only** - Use v1, v2, v3 (not v1.1, v1.2)
|
||
|
|
3. **Long deprecation periods** - Give clients time to migrate (6-12 months)
|
||
|
|
4. **Clear communication** - Use headers, docs, emails
|
||
|
|
5. **Maintain old versions** - Support at least 2 versions simultaneously
|
||
|
|
6. **Document changes** - Provide detailed migration guides
|
||
|
|
7. **Use semantic versioning** - For internal/SDK versioning
|
||
|
|
8. **Never break without warning** - Always announce breaking changes
|
||
|
|
9. **Provide tools** - Migration scripts, updated SDKs
|
||
|
|
10. **Monitor usage** - Track which versions are being used
|
||
|
|
|
||
|
|
## Anti-Patterns
|
||
|
|
|
||
|
|
Avoid these mistakes:
|
||
|
|
|
||
|
|
- **Breaking changes without version bump** - Breaks existing clients
|
||
|
|
- **Too many versions** - Maintenance nightmare (max 2-3 active versions)
|
||
|
|
- **Short deprecation periods** - Frustrates developers
|
||
|
|
- **No migration path** - Makes upgrades painful
|
||
|
|
- **Surprise sunsets** - Breaks production apps without warning
|
||
|
|
- **Inconsistent versioning** - Different strategies for different endpoints
|
||
|
|
- **Versioning individual endpoints** - Use consistent version across API
|