---
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`
