API Versioning & Changes
API Versioning and Change Management Guide
Version Control Strategy
Versioning Schema
interface APIVersion {
major: number; // Breaking changes
minor: number; // Backward-compatible features
patch: number; // Bug fixes
status: 'alpha' | 'beta' | 'stable' | 'deprecated';
}
// Example: v2.1.3
const VERSION_PATTERN = /^v(\d+)\.(\d+)\.(\d+)$/;
URI Versioning
// Base URL structure
const API_URL = 'https://api.example.com/v{major}/{resource}';
// Version routing middleware
const versionRouter = (req: Request, res: Response, next: NextFunction) => {
const version = req.path.split('/')[1]; // e.g., 'v2'
const [major] = version.match(VERSION_PATTERN) || [];
req.apiVersion = {
major: parseInt(major),
implementation: getVersionImplementation(major)
};
next();
};
Change Classifications
1. Breaking Changes
Changes that require client updates.
interface BreakingChange {
type: 'BREAKING';
changes: {
removedFields?: string[];
modifiedFields?: {
field: string;
oldType: string;
newType: string;
}[];
removedEndpoints?: string[];
authenticationChanges?: string[];
};
migrationGuide: string;
effectiveDate: Date;
}
// Example breaking changes
const BREAKING_CHANGES = {
'v2.0.0': {
type: 'BREAKING',
changes: {
removedFields: ['deprecated_field'],
modifiedFields: [{
field: 'status',
oldType: 'string',
newType: 'enum'
}]
},
migrationGuide: 'docs/migration-v1-to-v2.md',
effectiveDate: new Date('2024-12-01')
}
};
2. Non-Breaking Changes
Backward-compatible additions or modifications.
interface NonBreakingChange {
type: 'NON_BREAKING';
changes: {
addedFields?: string[];
addedEndpoints?: string[];
optionalParameters?: string[];
enhancedResponses?: string[];
};
announcementDate: Date;
}
3. Bug Fixes
Corrections that don't affect the API contract.
interface BugFix {
type: 'BUG_FIX';
fixes: {
description: string;
affectedEndpoints: string[];
resolution: string;
}[];
deploymentDate: Date;
}
Version Implementation
Version Manager
class APIVersionManager {
private versions: Map<number, Implementation>;
constructor() {
this.versions = new Map();
this.initializeVersions();
}
private initializeVersions() {
// Register version implementations
this.versions.set(1, new V1Implementation());
this.versions.set(2, new V2Implementation());
}
public getImplementation(version: number): Implementation {
const impl = this.versions.get(version);
if (!impl) {
throw new Error(`Version ${version} not supported`);
}
return impl;
}
public isDeprecated(version: number): boolean {
return this.versions.get(version)?.status === 'deprecated';
}
}
Version-Specific Implementations
interface Implementation {
version: number;
status: 'active' | 'deprecated';
handlers: Map<string, RequestHandler>;
}
class V2Implementation implements Implementation {
version = 2;
status = 'active';
handlers = new Map([
['createVault', this.createVaultV2],
['getVault', this.getVaultV2]
]);
async createVaultV2(req: Request, res: Response) {
// V2 implementation
}
async getVaultV2(req: Request, res: Response) {
// V2 implementation
}
}
API Documentation
OpenAPI Versioning
openapi: 3.0.0
info:
version: 2.0.0
title: Vault API
servers:
- url: https://api.example.com/v2
paths:
/vaults:
post:
summary: Create Vault (v2)
description: |
Changes from v1:
- Added required 'security' field
- Modified response structure
components:
schemas:
VaultV2:
allOf:
- $ref: '#/components/schemas/VaultV1'
- type: object
properties:
security:
type: object
required: true
Change Management Process
1. Planning Phase
interface ChangeProposal {
type: 'BREAKING' | 'NON_BREAKING' | 'BUG_FIX';
description: string;
justification: string;
impact: {
clients: string[];
endpoints: string[];
estimatedEffort: string;
};
timeline: {
developmentStart: Date;
betaRelease: Date;
stableRelease: Date;
deprecationDate?: Date;
};
}
2. Communication Strategy
interface VersionAnnouncement {
version: string;
type: 'NEW_VERSION' | 'DEPRECATION' | 'END_OF_LIFE';
details: {
summary: string;
changes: string[];
migrationGuide?: string;
timeline: {
announcementDate: Date;
effectiveDate: Date;
endOfSupportDate?: Date;
};
};
distributionChannels: string[];
}
3. Implementation Guidelines
Version Transition
// Graceful version handling
app.use('/v:version/*', (req, res, next) => {
const version = parseInt(req.params.version);
if (version < MINIMUM_SUPPORTED_VERSION) {
return res.status(410).json({
error: 'Version no longer supported',
minimumVersion: MINIMUM_SUPPORTED_VERSION,
migrationGuide: getMigrationGuideUrl(version)
});
}
if (isDeprecated(version)) {
res.setHeader('Warning', '299 - "Deprecated API Version"');
}
next();
});
Feature Flags
interface FeatureFlag {
name: string;
enabled: boolean;
version: number;
rolloutPercentage: number;
conditions?: {
clientIds?: string[];
environments?: string[];
startDate?: Date;
};
}
const featureManager = {
isEnabled: (feature: string, context: RequestContext): boolean => {
const flag = getFeatureFlag(feature);
if (!flag) return false;
return (
flag.enabled &&
meetsVersionRequirement(context.version, flag.version) &&
meetsRolloutCriteria(context, flag.rolloutPercentage) &&
meetsConditions(context, flag.conditions)
);
}
};
Deprecation Strategy
1. Deprecation Timeline
interface DeprecationSchedule {
version: string;
announceDate: Date;
deprecationDate: Date;
endOfLifeDate: Date;
alternativeVersion: string;
migrationDeadline: Date;
}
const DEPRECATION_POLICY = {
minimumNoticePeroid: 180, // days
supportWindow: 365, // days
maxVersionsSupported: 2
};
2. Deprecation Notices
// Response headers
app.use((req, res, next) => {
const version = req.apiVersion;
if (isDeprecated(version)) {
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', getEndOfLifeDate(version));
res.setHeader('Link', `<${getUpgradeGuideUrl(version)}>; rel="deprecation"`);
}
next();
});
3. Migration Support
interface MigrationTool {
validateRequest: (req: Request, targetVersion: number) => ValidationResult;
transformRequest: (req: Request, sourceVersion: number, targetVersion: number) => Request;
transformResponse: (res: Response, sourceVersion: number, targetVersion: number) => Response;
generateMigrationPlan: (currentVersion: number, targetVersion: number) => MigrationStep[];
}
Monitoring and Analytics
Version Usage Tracking
interface VersionMetrics {
version: string;
requests: number;
uniqueClients: number;
errorRate: number;
avgResponseTime: number;
deprecationStatus: 'active' | 'deprecated' | 'sunset';
}
const trackVersionUsage = async (req: Request, res: Response, next: NextFunction) => {
const startTime = Date.now();
const version = req.apiVersion;
res.on('finish', () => {
const duration = Date.now() - startTime;
metrics.recordVersionUsage({
version,
duration,
status: res.statusCode,
clientId: req.auth?.clientId
});
});
next();
};