Master Spring Boot 4 API Versioning: 4 Strategies & Real‑World Examples
Spring Boot 4 now natively supports API versioning with four strategies—Path Segment, Request Header, Query Parameter, and Media Type—offering intuitive configuration, smart routing, and seamless content negotiation, and the article provides detailed setup, code samples, and best‑practice guidance for reliable version control.
Earlier this year the author introduced the API versioning support in the Spring Boot 4.0 snapshot. With the imminent release of Spring Boot 4.0, the related APIs and usage have changed dramatically—configuration is more intuitive, routing is smarter, and the overall experience is smoother.
Spring Boot 4 (based on Spring Framework 7) moves API versioning down to the framework layer, so routing, content negotiation, and default version handling are provided by the framework itself, eliminating the need for custom code.
This article uses the latest Spring Boot 4.0 RC2 to comprehensively explain the newest usage of this feature, covering strategy selection, configuration methods, and practical examples to help you implement reliable and maintainable version control in real projects.
Four version control strategies
Spring Boot 4 covers the four most common versioning approaches, suitable for both public APIs and internal micro‑service scenarios. The following user‑service example demonstrates each strategy and its trade‑offs.
1. Path versioning (Path Segment)
The version number appears directly in the URL path, making it easy for callers to identify the API version and for logs to be clear.
Spring MVC captures the version via a path variable such as /{version} and passes it to the version resolver.
Applicable scenario: Public or mobile REST APIs that need quick testing in documentation or browsers.
Example URL:
GET /api/v1/users
GET /api/v2/usersController implementation:
package com.pig4cloud.pigx.version.demo;
/**
* Pig User Management Controller – Path versioning example
*/
@RestController
@RequestMapping("/api")
public class PigUserController {
@Autowired
private PigUserRepository pigUserRepository;
@Autowired
private PigUserMapper pigUserMapper;
@GetMapping(value = "/{version}/users", version = "1.0")
public List<PigUserDTOv1> findAllv1() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV1)
.collect(Collectors.toList());
}
@GetMapping(value = "/{version}/users", version = "2.0")
public List<PigUserDTOv2> findAllv2() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV2)
.collect(Collectors.toList());
}
}2. Request‑header versioning
The version is passed via a custom HTTP header, keeping the URL unchanged and avoiding multiple paths for the same resource.
This approach aligns with REST semantics—resources are identified by URI, while version is part of the request context.
Applicable scenario: Internal service communication, BFF, GraphQL gateways, or any situation that requires stable URLs.
Example request:
# curl example
curl -H "X-API-Version: 1.0" http://localhost:8080/api/users
curl -H "X-API-Version: 2.0" http://localhost:8080/api/usersController implementation:
package com.pig4cloud.pigx.version.demo;
/**
* Pig User Management Controller – Header versioning example
*/
@RestController
@RequestMapping("/api")
public class PigUserController {
@Autowired
private PigUserRepository pigUserRepository;
@Autowired
private PigUserMapper pigUserMapper;
@GetMapping(value = "/users", version = "1.0")
public List<PigUserDTOv1> getUsersV1() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV1)
.collect(Collectors.toList());
}
@GetMapping(value = "/users", version = "2.0")
public List<PigUserDTOv2> getUsersV2() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV2)
.collect(Collectors.toList());
}
}Configuration:
spring.mvc.apiversion.use.header=X-API-Version3. Query‑parameter versioning
The version is supplied as a URL query parameter, e.g., ?version=1.0, balancing readability and flexibility. Most browsers and documentation tools can directly append the parameter for testing.
Applicable scenario: Backend admin tools or SDKs that need to switch versions temporarily or compare multiple versions.
Example URL:
GET /api/users?version=1.0
GET /api/users?version=2.0Controller implementation:
package com.pig4cloud.pigx.version.demo;
/**
* Pig User Management Controller – Query‑parameter versioning example
*/
@RestController
@RequestMapping("/api")
public class PigUserController {
@Autowired
private PigUserRepository pigUserRepository;
@Autowired
private PigUserMapper pigUserMapper;
@GetMapping(value = "/users", params = "version=1.0")
public List<PigUserDTOv1> listUsersV1(@RequestParam String version) {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV1)
.collect(Collectors.toList());
}
@GetMapping(value = "/users", params = "version=2.0")
public List<PigUserDTOv2> listUsersV2(@RequestParam String version) {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV2)
.collect(Collectors.toList());
}
}Configuration:
spring.mvc.apiversion.use.query-parameter=version4. Media‑type versioning
Leveraging HTTP content‑negotiation, the version is added as a parameter to the Accept header (e.g., Accept: application/json;version=1.0). The URL stays unchanged while the media type conveys version semantics.
Applicable scenario: Enterprise systems that strictly follow HTTP specifications or heavily use content negotiation.
Example request:
# curl example
curl -H "Accept: application/json;version=1.0" http://localhost:8080/api/users
curl -H "Accept: application/json;version=2.0" http://localhost:8080/api/usersController implementation:
package com.pig4cloud.pigx.version.demo;
/**
* Pig User Management Controller – Media‑type versioning example
*/
@RestController
@RequestMapping("/api")
public class PigUserController {
@Autowired
private PigUserRepository pigUserRepository;
@Autowired
private PigUserMapper pigUserMapper;
@GetMapping(value = "/users", version = "1.0", produces = "application/json")
public List<PigUserDTOv1> getUsersMediaV1() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV1)
.collect(Collectors.toList());
}
@GetMapping(value = "/users", version = "2.0", produces = "application/json")
public List<PigUserDTOv2> getUsersMediaV2() {
return pigUserRepository.findAll()
.stream()
.map(pigUserMapper::toV2)
.collect(Collectors.toList());
}
}Configuration:
spring.mvc.apiversion.use.media-type-parameter[application/json]=versionConfiguration methods
Spring Boot 4 offers two configuration approaches: Java‑code configuration and property‑file configuration.
Method 1: Java configuration (WebMvcConfigurer)
Implement WebMvcConfigurer and override configureApiVersioning to define supported versions, default version, and enabled strategies.
package com.pig4cloud.pigx.version.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.ApiVersionConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class PigWebConfig implements WebMvcConfigurer {
@Override
public void configureApiVersioning(ApiVersionConfigurer configurer) {
configurer
.addSupportedVersions("1.0", "2.0")
.setDefaultVersion("1.0")
.useRequestHeader("X-API-Version")
.useQueryParam("version")
.useMediaTypeParameter(MediaType.APPLICATION_JSON, "version");
}
}This method centralises version‑related decisions and can be adjusted dynamically via profiles or environments.
Method 2: Property configuration (application.properties)
For simple scenarios, configuration can be expressed entirely in application.properties without writing code.
# Basic configuration
spring.mvc.apiversion.supported=1.0,2.0
spring.mvc.apiversion.default=1.0
# Path versioning (e.g., /api/v1/users)
spring.mvc.apiversion.use.path-segment=1
# Header versioning
spring.mvc.apiversion.use.header=X-API-Version
# Query‑parameter versioning
spring.mvc.apiversion.use.query-parameter=version
# Media‑type versioning
spring.mvc.apiversion.use.media-type-parameter[application/json]=versionWhile simple, property configuration still supports combined strategies, allowing teams to enable only the needed mechanisms per environment.
Custom version parser
If an organization uses custom version formats such as v1, beta, etc., a custom ApiVersionParser can translate them into semantic versions recognizable by the framework.
package com.pig4cloud.pigx.version.demo;
import org.springframework.web.accept.ApiVersionParser;
/**
* Custom API version parser supporting formats like "v1" or "v2"
*/
public class PigApiVersionParser implements ApiVersionParser {
@Override
public Comparable parseVersion(String version) {
// Strip leading "v" or "V"
if (version.startsWith("v") || version.startsWith("V")) {
version = version.substring(1);
}
// Append ".0" if missing minor version
if (!version.contains(".")) {
version = version + ".0";
}
return version;
}
}This parser normalises custom formats so that ApiVersionConfigurer can sort them semantically.
Important limitation
Spring Boot 4 follows a "Path first" rule: once Path Segment versioning is enabled, other strategies are short‑circuited to avoid ambiguous matches.
Cannot mix Path versioning with other strategies Path Segment is primarily for public APIs; when it is active, Spring resolves the version from the URL and ignores header, query‑parameter, and media‑type strategies.
Therefore, decide during architecture design which strategies to enable for external versus internal APIs to avoid configuration conflicts.
Testing API version
After configuration, prepare a standardized set of call examples for developers, testers, and documentation platforms. Below are curl commands for the four strategies:
# Path versioning
curl http://localhost:8080/api/v1/users
curl http://localhost:8080/api/v2/users
# Header versioning
curl -H "X-API-Version: 1.0" http://localhost:8080/api/users
curl -H "X-API-Version: 2.0" http://localhost:8080/api/users
# Query‑parameter versioning
curl "http://localhost:8080/api/users?version=1.0"
curl "http://localhost:8080/api/users?version=2.0"
# Media‑type versioning
curl -H "Accept: application/json;version=1.0" http://localhost:8080/api/users
curl -H "Accept: application/json;version=2.0" http://localhost:8080/api/usersSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
