--- 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
Middleware participant Controller as Version-Aware
Controller participant Service Client->>Middleware: Request with Accept header
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
Add Deprecation Headers Deprecated --> Sunset: Sunset Date Reached
Stop Accepting New Requests Sunset --> Removed: Grace Period Ended
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
v2 adoption?} Phase2 -->|No| Wait1 Phase2 -->|Yes| Phase2Start[Phase 2: Deprecate v1] Phase2Start --> AddHeaders[Add deprecation headers to v1] AddHeaders --> NotifyClients[Notify clients via
deprecation warnings] NotifyClients --> ProvideGuide[Provide migration guide] ProvideGuide --> Monitor2[Monitor migration progress] Monitor2 --> Wait2[Wait until sunset date] Wait2 --> Phase3{Sunset date
reached?} Phase3 -->|No| Monitor2 Phase3 -->|Yes| Phase3Start[Phase 3: Remove v1] Phase3Start --> StopAccepting[Stop accepting new v1 requests] StopAccepting --> GracePeriod[Grace period for
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: ; 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`