Files
pos-system/docs/en/skills/api-versioning-strategy.md
Ho Ngoc Hai 2640b351c3 Enhance documentation with detailed diagrams and structured flows
- Added request/response flow diagrams to api-design and api-gateway-advanced skills for better visualization of processes.
- Introduced configuration loading flow in configuration-management skill to clarify the configuration process.
- Included error propagation flow in error-handling-patterns skill to illustrate error handling across layers.
- Enhanced various skills with additional diagrams to improve understanding of complex concepts.

These updates aim to provide clearer guidance and improve the overall documentation experience for developers.
2026-01-01 23:22:54 +07:00

405 lines
12 KiB
Markdown

---
name: api-versioning-strategy
description: API versioning strategies for GoodGo microservices including semantic versioning, backward compatibility patterns, API deprecation, version negotiation, and breaking changes handling.
---
# API Versioning Strategy
## When to Use This Skill
Use this skill when:
- Versioning APIs
- Handling breaking changes
- Implementing API deprecation
- Maintaining backward compatibility
- Implementing version negotiation
- Managing multiple API versions
- Planning API evolution
- Communicating API changes to consumers
## Core Concepts
### Versioning Strategies
1. **URL Path Versioning**: `/api/v1/users`, `/api/v2/users`
2. **Header Versioning**: `Accept: application/vnd.goodgo.v1+json`
3. **Query Parameter**: `/api/users?version=1`
4. **Semantic Versioning**: Major.Minor.Patch (e.g., 1.2.3)
### Compatibility Types
- **Backward Compatible**: New version works with old clients
- **Forward Compatible**: Old version works with new clients
- **Breaking Changes**: Incompatible changes requiring new version
## Version Negotiation
Version negotiation allows clients to request a specific API version through headers while maintaining clean URLs. The middleware extracts the version from the `Accept` header and routes to the appropriate handler.
```mermaid
sequenceDiagram
participant Client
participant Middleware as Version Negotiation<br/>Middleware
participant Controller as Version-Aware<br/>Controller
participant Service
Client->>Middleware: Request with Accept header<br/>Accept: application/vnd.goodgo.v1+json
Middleware->>Middleware: Extract version from header
alt Version specified
Middleware->>Middleware: Parse version number
alt Version supported
Middleware->>Controller: Set req.apiVersion = 1
Controller->>Controller: Check version
Controller->>Service: Call service method
Service-->>Controller: Return data
Controller->>Controller: Format response for v1
Controller-->>Client: v1 response format
else Version not supported
Middleware-->>Client: 400 Unsupported Version
end
else No version specified
Middleware->>Controller: Set req.apiVersion = latest (2)
Controller->>Service: Call service method
Service-->>Controller: Return data
Controller->>Controller: Format response for v2
Controller-->>Client: v2 response format (default)
end
```
### Implementation
```typescript
// src/middlewares/version-negotiation.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { logger } from '@goodgo/logger';
export function versionNegotiation(
req: Request,
res: Response,
next: NextFunction
): void {
const acceptHeader = req.headers.accept || '';
const versionMatch = acceptHeader.match(/application\/vnd\.goodgo\.v(\d+)\+json/);
if (versionMatch) {
const requestedVersion = parseInt(versionMatch[1], 10);
req.apiVersion = requestedVersion;
const supportedVersions = [1, 2];
if (!supportedVersions.includes(requestedVersion)) {
return res.status(400).json({
success: false,
error: {
code: 'UNSUPPORTED_VERSION',
message: `API version ${requestedVersion} is not supported. Supported versions: ${supportedVersions.join(', ')}`,
},
});
}
} else {
req.apiVersion = 2; // Default to latest
}
next();
}
```
## API Deprecation Timeline
API deprecation follows a structured timeline to give consumers adequate time to migrate. The lifecycle progresses through active, deprecated, sunset, and removed phases.
```mermaid
gantt
title API Version Lifecycle Timeline
dateFormat YYYY-MM-DD
section Version 1
Active (v1 only) :active, v1-active, 2024-01-01, 2024-06-01
Deprecated (v1 + v2) :crit, v1-deprecated, 2024-06-01, 2024-12-31
Sunset Period :v1-sunset, 2024-12-31, 2025-01-31
Removed :v1-removed, 2025-01-31, 1d
section Version 2
Development :v2-dev, 2024-03-01, 2024-06-01
Active (v1 + v2) :active, v2-active, 2024-06-01, 2025-12-31
```
### Deprecation Phases
```mermaid
stateDiagram-v2
[*] --> Active: Version Released
Active --> Deprecated: New Version Released<br/>Add Deprecation Headers
Deprecated --> Sunset: Sunset Date Reached<br/>Stop Accepting New Requests
Sunset --> Removed: Grace Period Ended<br/>Remove Routes
Removed --> [*]
note right of Active
- Version fully supported
- No warnings
- All features available
end note
note right of Deprecated
- Deprecation header set
- Warning headers added
- Migration guide provided
- Still functional
end note
note right of Sunset
- Read-only mode
- No new requests accepted
- Existing requests honored
end note
```
### Deprecation Headers
```typescript
// src/middlewares/deprecation.middleware.ts
export function deprecationMiddleware(version: string, sunsetDate: string) {
return (req: Request, res: Response, next: NextFunction): void => {
if (req.apiVersion && parseInt(req.apiVersion.toString()) < parseInt(version)) {
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', sunsetDate);
res.setHeader('Link', `<${req.url.replace(/\/v\d+/, `/v${version}`)}>; rel="successor-version"`);
res.setHeader('Warning', `299 - "API version ${req.apiVersion} is deprecated. Please migrate to version ${version} by ${sunsetDate}"`);
}
next();
};
}
```
## Migration Flow
Breaking changes require a careful 3-phase migration strategy to ensure zero downtime and smooth client transitions.
```mermaid
flowchart TD
Start([Breaking Change Identified]) --> Phase1[Phase 1: Support Both Versions]
Phase1 --> DeployV2[Deploy v2 alongside v1]
DeployV2 --> Monitor1[Monitor v1 and v2 usage]
Monitor1 --> Wait1[Wait for client adoption]
Wait1 --> Phase2{Sufficient<br/>v2 adoption?}
Phase2 -->|No| Wait1
Phase2 -->|Yes| Phase2Start[Phase 2: Deprecate v1]
Phase2Start --> AddHeaders[Add deprecation headers to v1]
AddHeaders --> NotifyClients[Notify clients via<br/>deprecation warnings]
NotifyClients --> ProvideGuide[Provide migration guide]
ProvideGuide --> Monitor2[Monitor migration progress]
Monitor2 --> Wait2[Wait until sunset date]
Wait2 --> Phase3{Sunset date<br/>reached?}
Phase3 -->|No| Monitor2
Phase3 -->|Yes| Phase3Start[Phase 3: Remove v1]
Phase3Start --> StopAccepting[Stop accepting new v1 requests]
StopAccepting --> GracePeriod[Grace period for<br/>existing requests]
GracePeriod --> RemoveRoutes[Remove v1 routes]
RemoveRoutes --> End([Migration Complete])
style Phase1 fill:#e1f5ff
style Phase2Start fill:#fff4e1
style Phase3Start fill:#ffe1e1
style End fill:#e1ffe1
```
### Implementation Strategy
```typescript
// src/core/api/migration.strategy.ts
export class MigrationStrategy {
/**
* Phase 1: Support both versions
*/
phase1SupportBoth(): void {
router.use('/v1', v1Router);
router.use('/v2', v2Router);
}
/**
* Phase 2: Deprecate v1
*/
phase2DeprecateV1(): void {
router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
router.use('/v2', v2Router);
}
/**
* Phase 3: Remove v1
*/
phase3RemoveV1(): void {
router.use('/v2', v2Router);
// v1 routes removed after sunset date
}
}
```
## URL Path Versioning
### Implementation
```typescript
// src/routes/index.ts
import { Router } from 'express';
import v1Router from './v1';
import v2Router from './v2';
const router = Router();
router.use('/v1', v1Router);
router.use('/v2', v2Router);
export default router;
```
## Semantic Versioning
### Version Structure
```
MAJOR.MINOR.PATCH
MAJOR: Breaking changes
MINOR: Backward-compatible additions
PATCH: Backward-compatible bug fixes
```
### Version Response
```typescript
// src/core/api/version.middleware.ts
export function versionMiddleware(req: Request, res: Response, next: NextFunction): void {
const originalJson = res.json.bind(res);
res.json = (data: any) => {
const response = {
...data,
metadata: {
...data.metadata,
apiVersion: req.apiVersion || '2.0.0',
serviceVersion: process.env.SERVICE_VERSION || '1.0.0',
},
};
return originalJson(response);
};
next();
}
```
## Backward Compatibility
### Compatibility Layer
```typescript
// src/core/api/compatibility.adapter.ts
export class CompatibilityAdapter {
adaptV1ToV2(v1Data: any): any {
return {
success: true,
data: {
user: {
...v1Data,
profile: null, // Add default for new field
},
},
metadata: {
version: '2.0.0',
adapted: true,
},
};
}
adaptV2RequestToV1(v2Request: any): any {
return {
email: v2Request.email,
name: v2Request.name,
// Ignore new fields
};
}
}
```
## Best Practices
1. **Versioning Strategy**: Choose URL path or header, be consistent
2. **Semantic Versioning**: Use MAJOR.MINOR.PATCH
3. **Deprecation**: Always deprecate before removing
4. **Migration Guide**: Provide clear migration documentation
5. **Backward Compatibility**: Maintain compatibility when possible
6. **Communication**: Clearly communicate version changes
## Common Mistakes
1. **No Deprecation Period**: Breaking clients suddenly
```typescript
// ❌ BAD: Remove v1 immediately
router.use('/v2', v2Router);
// ✅ GOOD: Deprecate with sunset date
router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
router.use('/v2', v2Router);
```
2. **Breaking Changes Without Major Version**: Client confusion
```
# ❌ BAD: Breaking change in minor version
v1.1.0 → Changed response format
# ✅ GOOD: Breaking change = new major version
v1.x.x → v2.0.0 (new response format)
```
3. **Inconsistent Versioning Strategy**: Mixed approaches
```typescript
// ❌ BAD: Mix URL and header versioning
/api/v1/users + Accept: application/vnd.v2+json
// ✅ GOOD: Choose one approach
/api/v1/users OR Accept: application/vnd.goodgo.v1+json
```
## Quick Reference
| Strategy | Pros | Cons | Use When |
|----------|------|------|----------|
| **URL Path** | Clear, cacheable | URL changes | Public APIs |
| **Header** | Clean URLs | Less visible | Internal APIs |
| **Query Param** | Simple | Not RESTful | Quick prototypes |
**Semantic Versioning:**
```
MAJOR.MINOR.PATCH
│ │ └── Bug fixes (backward compatible)
│ └──────── New features (backward compatible)
└────────────── Breaking changes
```
**Version Lifecycle:**
```
v1 Active → v2 Released → v1 Deprecated → v1 Sunset → v1 Removed
│ │ │ │ │
│ │ Add headers Remove from Delete
│ Support + warnings docs routes
Solo both
```
**Deprecation Headers:**
```http
Deprecation: true
Sunset: Sat, 31 Dec 2024 23:59:59 GMT
Warning: 299 - "API v1 is deprecated. Migrate to v2 by 2024-12-31"
Link: </api/v2/users>; rel="successor-version"
```
## Resources
- [API Design](./api-design.md) - API design patterns
- [Middleware Patterns](./middleware-patterns.md) - Middleware patterns
- [Project Rules](./project-rules.md) - GoodGo standards
- Skill Source: `.cursor/skills/api-versioning-strategy/SKILL.md`