Building REST APIs
API Versioning Strategies — URL, Header, and Content Negotiation
Pros and cons of API routing version patterns in production.
Introduction
As business requirements change, API endpoints evolve. Modifying an active endpoint with breaking changes will break existing integrations (e.g., mobile apps, external clients). API versioning allows developers to introduce updates while maintaining backward compatibility for legacy clients. In Spring Boot, this is achieved through URL path routing, custom request headers, or Accept headers.
Why It Matters
Choosing the right versioning strategy dictates how your API is cached, integrated, and monitored. Using the wrong strategy can lead to reverse-proxy caching bugs, overly complex URL routing setups, or integration headaches for mobile developers.
Real-World Analogy
Think of updating a government application form:
- URL Versioning: Form v1 and Form v2 are printed on physically separate papers on separate desks. It is clear and visible, and anyone can grab the version they need directly.
- Custom Header: You fill out the form, but are required to write "v2" on a sticky note attached to the envelope. The postman must open and read the note to route it.
- Accept Header (Content Negotiation): You request the form at the customer service desk, but verbally state your preference: "Please hand me the document matching the 2026 version standard."
How It Works
Here is a comparison of the three primary API versioning strategies:
| Strategy | URL Sample | Caching Support | Description |
|---|---|---|---|
| URL Versioning | /api/v1/products |
Excellent (Native) | Easiest to debug, routes easily on proxy servers. Highly visible. |
| Custom Header | /api/products (Header: X-API-VERSION=2) |
Requires Config | Keeps URIs clean, but requires client to inject headers. |
| Accept Header | /api/products (Header: Accept=application/vnd.company-v2+json) |
Requires Config | Strict RESTful content negotiation standard. Harder to debug. |
Practical Example
Here is how to map endpoints in a Spring Controller using each of the three strategies:
Quick Quiz
Q1: Which versioning strategy is easiest to cache using standard HTTP reverse proxy caches (like Cloudflare or Squid) without custom rules?
A) Custom Request Header versioning
B) URL path versioning
C) Media Type accept header versioning
D) Query parameter versioning
Answer: B — Proxies cache responses based on the request URL path by default. Changing paths ensures caches remain segregated without extra headers setup.
Q2: What attribute parameter is used in @GetMapping to match the accept header media type?
A) headers
B) produces
C) consumes
D) value
Answer: B — The produces attribute restricts mapping to requests where the Accept header matches the media type, defining the response format.
Scenario-Based Challenge
Production Scenario:
Your team is building a public API used by third-party developer partners. You must support at least three active versions of the API for several years. The primary goals are simplicity of documentation (Swagger/OpenAPI) and simplicity of testing using standard browsers. Which versioning strategy is most suitable?
View Solution
URL path versioning (e.g. /api/v1/products) is the best choice here.
It makes documenting versions in OpenAPI straightforward as separate paths. It allows developers to test endpoints directly by opening URLs in their browser address bar without injecting custom headers. It also simplifies routing at the API Gateway level (e.g., routing /v1/* traffic to Service A and /v2/* to Service B).
Interview Questions
1. How can you inform clients that an API version is deprecated and will be discontinued?
Standard practice is to append official HTTP headers to responses of deprecated endpoints: the Deprecation header (signaling the endpoint is deprecated) and the Sunset header (specifying the exact date/timestamp when the service version will be shut down).
2. What is the URI pollution trade-off in REST versioning?
URL versioning "pollutes" the URL namespace because the same resource (e.g., a product with id 42) has multiple URLs: /api/v1/products/42 and /api/v2/products/42, which violates the strict REST principle that a resource should have exactly one unique identifier URI. Custom header and Accept header versioning keep the URL clean (/api/products/42), resolving URI pollution.
Production Considerations
Implement a strict deprecation policy. Never maintain more than 2 or 3 active versions simultaneously. Supporting too many legacy versions causes code maintenance overhead, complicates database schema migration paths, and delays the adoption of newer performance optimizations.