Master API versioning techniques — URL versioning, header versioning, and deprecation strategies for maintaining backwards-compatible APIs.

Abdur Razzak
Full-Stack Web Developer
As your API evolves, you will need to make breaking changes — rename fields, change response shapes, remove endpoints. Without versioning, these changes break existing clients. Versioning lets you introduce new API versions while keeping old ones running for clients that haven't upgraded. This is essential for public APIs, mobile apps (where users may not update for months), and B2B integrations.
The most common approach: include the version in the URL path — /api/v1/users, /api/v2/users. Easy to implement, explicit, and cacheable. Clients know exactly which version they are using. The downside: URLs change when versions change, and it can feel inelegant. In Express, mount version-specific routers: app.use('/api/v1', v1Router); app.use('/api/v2', v2Router).
Header versioning keeps URLs clean by specifying the version in a custom header: API-Version: 2 or via Accept header: Accept: application/vnd.api+json;version=2. This is RESTfully correct (the same resource has one URL) but harder to test in a browser and can complicate caching. GitHub's API uses header versioning. It is more sophisticated but requires more coordination with API consumers.
In Express, organize version-specific route files and import them with a version prefix. In NestJS, use the built-in versioning: app.enableVersioning({ type: VersioningType.URI }) and decorate controllers with @Version('1') or @Version('2'). NestJS handles the routing automatically. Both frameworks support middleware that reads the version from a header and routes accordingly.
Never remove an API version without warning. Add a Deprecation header and Sunset header to responses from deprecated API versions: Deprecation: true, Sunset: Sat, 31 Dec 2025 23:59:59 GMT. Communicate the sunset date to all consumers well in advance. Monitor usage of deprecated endpoints to know when it is safe to remove them. Keep deprecated versions running for at least 6 months after the sunset announcement.
The best versioning strategy is minimizing the need for it. Follow additive evolution: add new fields rather than removing or renaming existing ones. Use nullable fields rather than required fields for new additions. Follow the Robustness Principle: be strict in what you send and lenient in what you accept. Document your API contract with OpenAPI/Swagger from the start and treat it as a binding commitment to your API consumers.