Spring Boot 4 API Versioning with Path Segments

API versioning is essential when your API evolves and you need to support multiple clients without breaking existing integrations, and Spring Boot 4 makes path-based versioning straightforward with built-in support.

Why API versioning matters

When a REST API is shared by multiple clients, a small change in the contract can turn into a production outage. If you change the response structure, rename a field, or tighten validation rules, older clients may break unexpectedly.

That is why versioning is often part of long-lived API design. It gives teams a controlled way to introduce new behavior while continuing to support older clients.

The Spring team has now added official API versioning support in the Spring ecosystem, and the pattern is very practical for Spring Boot applications. The goal is not to reinvent custom routing logic in every controller, but to let the framework handle version resolution consistently.

The path-based approach

Path-based versioning is still the easiest style to understand and debug. It is explicit, visible in the URL, and simple for clients and support teams to reason about.

A common pattern is:

1
2
/api/v1/greeting
/api/v2/greeting

The version is part of the URL path, so a client can clearly see which contract it is using.

This is the exact pattern used in the sample project at SpringBoot/api-versioning: version is read from a path segment, and the controller version is selected by Spring based on the request URL.

Spring Boot 4 configuration

The key configuration is ApiVersionConfigurer with usePathSegment(1). This tells Spring that the version lives in the first path segment after the base path.

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void configureApiVersioning(ApiVersionConfigurer configurer) {
configurer
.usePathSegment(1)
.addSupportedVersions("1", "2")
.setDefaultVersion("1");
}
}

This is equivalent to the property-based setup:

1
2
3
4
5
spring.mvc.api-versioning.enabled=true
spring.mvc.api-versioning.path-segment.enabled=true
spring.mvc.api-versioning.path-segment.index=1
spring.mvc.api-versioning.supported-versions=1,2
spring.mvc.api-versioning.default-version=1

In plain terms, Spring expects a URL like /api/v1/... and resolves the version from that segment automatically.

Controller example

The controller in the sample project uses a route prefix with {version} in the path:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/api/v{version}")
public class GreetingController {

// /api/v1/greeting
@GetMapping(path = "/greeting", version = "1")
public GreetingResponse greetingV1() {
return new GreetingResponse("1", "Hello from API version 1", List.of("basic-message"));
}

// /api/v2/greeting
@GetMapping(path = "/greeting", version = "2")
public GreetingResponse greetingV2() {
return new GreetingResponse("2", "Hello from API version 2", List.of("basic-message", "metadata"));
}
}

This is a clean pattern because the version appears in the URL and the handler method declares the matching API version explicitly.

What Spring is doing for us

With this setup, the framework does the heavy lifting:

  • it reads the version from the URL path segment
  • it validates whether that version is supported
  • it matches the request to the correct controller method
  • it falls back to the configured default version when needed

This means we no longer need to write custom logic such as if (version.equals("v1")) { ... } throughout the controller layer.

Example requests

Once configured, the endpoints are very easy to call:

1
2
curl http://localhost:8080/api/v1/greeting
curl http://localhost:8080/api/v2/greeting

Example responses might look like this:

1
2
3
4
5
{
"version": "1",
"message": "Hello from API version 1",
"capabilities": ["basic-message"]
}
1
2
3
4
5
{
"version": "2",
"message": "Hello from API version 2",
"capabilities": ["basic-message", "metadata"]
}

Why path-based versioning is a strong choice

For public-facing APIs, path-based versioning has a lot of advantages:

  • easy to read in logs and debug output
  • obvious to clients and developers
  • simple to document in OpenAPI and API specifications
  • easy to test in staging and production environments

If a client calls /api/v1/..., it is immediately clear which API contract is in use.

When to prefer header-based versioning instead

Path-based versioning is great for visibility, but some teams prefer headers for cleaner URLs. Spring also supports header-based versioning, but that is a different style.

For example, you could configure:

1
configurer.useRequestHeader("X-API-Version");

This keeps the route clean, such as /api/greeting, while the version is provided in a request header. The trade-off is that it is less visible and can be easier to forget when troubleshooting requests.

For most public APIs, I still prefer path-based versioning because the contract is obvious.

Best practices for Spring Boot API versioning

When using path-based versioning, a few practices make the API easier to maintain:

  1. Keep the version in a stable prefix, such as /api/v1/... and /api/v2/....
  2. Avoid mixing version-specific logic into unrelated business code.
  3. Use separate controller logic or service boundaries per major version when breaking changes are required.
  4. Plan a migration window before removing an old version.
  5. Document which versions are supported and when they are deprecated.

This approach keeps versioning predictable and makes it much easier to support old clients while introducing new functionality.

Final thoughts

Spring Boot 4 provides a clean, built-in way to support API versioning at the framework level, and path-segment versioning is the most natural and readable strategy for many teams.

The pattern from the sample project is simple but powerful:

  • configure the version position using usePathSegment(1)
  • declare the version on the request mapping
  • expose URLs such as /api/v1/... and /api/v2/...
  • let Spring resolve the correct handler automatically

If you are building a public API that needs long-term compatibility, path-based versioning is still one of the clearest and most maintainable choices.

Resources