---
trigger: always_on
---

# 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

## URL Path Versioning

### Implementation

```typescript
// src/routes/index.ts
// EN: Route versioning
// VI: Route versioning
import { Router } from 'express';
import v1Router from './v1';
import v2Router from './v2';

const router = Router();

// EN: Version 1 routes
// VI: Routes version 1
router.use('/v1', v1Router);

// EN: Version 2 routes
// VI: Routes version 2
router.use('/v2', v2Router);

export default router;
```

### Version Router

```typescript
// src/routes/v1/index.ts
// EN: Version 1 routes
// VI: Routes version 1
import { Router } from 'express';
import userRoutes from './users';

const router = Router();

router.use('/users', userRoutes);

export default router;

// src/routes/v2/index.ts
// EN: Version 2 routes with breaking changes
// VI: Routes version 2 với breaking changes
import { Router } from 'express';
import userRoutes from './users'; // EN: Different implementation / VI: Implementation khác

const router = Router();

router.use('/users', userRoutes); // EN: Different response format / VI: Format response khác

export default router;
```

## Header-Based Versioning

### Version Negotiation Middleware

```typescript
// src/middlewares/version-negotiation.middleware.ts
// EN: Version negotiation middleware
// VI: Middleware version negotiation
import { Request, Response, NextFunction } from 'express';
import { logger } from '@goodgo/logger';

export function versionNegotiation(
  req: Request,
  res: Response,
  next: NextFunction
): void {
  // EN: Extract version from Accept header
  // VI: Trích xuất version từ Accept header
  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;

    // EN: Check if version is supported
    // VI: Kiểm tra xem version có được hỗ trợ không
    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 {
    // EN: Default to latest version
    // VI: Mặc định version mới nhất
    req.apiVersion = 2;
  }

  next();
}
```

### Version-Aware Controller

```typescript
// src/modules/user/user.controller.ts
// EN: Version-aware controller
// VI: Controller nhận biết version
export class UserController {
  async getUser(req: Request, res: Response): Promise<void> {
    const version = req.apiVersion || 2;

    if (version === 1) {
      // EN: Version 1 response format
      // VI: Format response version 1
      const user = await this.userService.findById(req.params.id);
      res.json({
        id: user.id,
        email: user.email,
        name: user.name,
      });
    } else {
      // EN: Version 2 response format
      // VI: Format response version 2
      const user = await this.userService.findById(req.params.id);
      res.json({
        success: true,
        data: {
          user: {
            id: user.id,
            email: user.email,
            name: user.name,
            profile: user.profile, // EN: New field in v2 / VI: Field mới trong v2
          },
        },
        metadata: {
          version: '2.0.0',
          timestamp: new Date().toISOString(),
        },
      });
    }
  }
}
```

## 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
// EN: Add version to response
// VI: Thêm version vào response
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();
}
```

## API Deprecation

### Deprecation Headers

```typescript
// src/middlewares/deprecation.middleware.ts
// EN: Deprecation warning middleware
// VI: Middleware cảnh báo deprecation
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();
  };
}

// Usage
router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
```

### Deprecation Documentation

```typescript
// src/docs/deprecation.md
// EN: Deprecation notices
// VI: Thông báo deprecation

## API Version 1 Deprecation

**Deprecated**: 2024-01-01  
**Sunset Date**: 2024-12-31  
**Migration Guide**: [Migration Guide](./migration-v1-to-v2.md)

### Changes in Version 2

- New response format
- Additional user profile fields
- Updated authentication flow
```

## Backward Compatibility

### Compatibility Layer

```typescript
// src/core/api/compatibility.adapter.ts
// EN: Backward compatibility adapter
// VI: Adapter tương thích ngược
export class CompatibilityAdapter {
  /**
   * EN: Adapt v1 response to v2 format
   * VI: Adapt response v1 sang format v2
   */
  adaptV1ToV2(v1Data: any): any {
    return {
      success: true,
      data: {
        user: {
          ...v1Data,
          profile: null, // EN: Add default for new field / VI: Thêm mặc định cho field mới
        },
      },
      metadata: {
        version: '2.0.0',
        adapted: true,
      },
    };
  }

  /**
   * EN: Adapt v2 request to v1 format
   * VI: Adapt request v2 sang format v1
   */
  adaptV2RequestToV1(v2Request: any): any {
    return {
      email: v2Request.email,
      name: v2Request.name,
      // EN: Ignore new fields / VI: Bỏ qua các field mới
    };
  }
}
```

## Breaking Changes Migration

### Migration Strategy

```typescript
// src/core/api/migration.strategy.ts
// EN: Migration strategy for breaking changes
// VI: Chiến lược migration cho breaking changes
export class MigrationStrategy {
  /**
   * EN: Phase 1: Support both versions
   * VI: Giai đoạn 1: Hỗ trợ cả hai versions
   */
  phase1SupportBoth(): void {
    // EN: Keep v1, add v2
    // VI: Giữ v1, thêm v2
    router.use('/v1', v1Router);
    router.use('/v2', v2Router);
  }

  /**
   * EN: Phase 2: Deprecate v1
   * VI: Giai đoạn 2: Deprecate v1
   */
  phase2DeprecateV1(): void {
    router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
    router.use('/v2', v2Router);
  }

  /**
   * EN: Phase 3: Remove v1
   * VI: Giai đoạn 3: Xóa v1
   */
  phase3RemoveV1(): void {
    // EN: After sunset date, remove v1 routes
    // VI: Sau sunset date, xóa v1 routes
    router.use('/v2', v2Router);
  }
}
```

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

4. **No Migration Guide**: Clients don't know how to upgrade
   ```
   # ✅ Always provide:
   - Changelog
   - Migration guide
   - Sunset date
   - Deprecation warnings
   ```

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

**Version Detection:**
```typescript
// URL path
const version = req.path.match(/\/v(\d+)\//)?.[1] || '2';

// Header
const version = req.headers.accept?.match(/vnd\.goodgo\.v(\d+)/)?.[1];
```