Backend Development 11 min read

Mastering API Versioning in Spring Framework 7.0: Strategies and Code Samples

Spring Framework 7.0 introduces native API versioning support, enabling developers to manage multiple API versions through URI, request header, query parameter, and content negotiation strategies, with detailed implementation examples, customizable resolvers, and client-side usage via WebClient, ensuring backward compatibility and flexible version control.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Mastering API Versioning in Spring Framework 7.0: Strategies and Code Samples

API versioning is a key practice in modern web development that lets developers evolve APIs without breaking existing clients.

Importance of API Versioning

Versioning allows new features, bug fixes, or data‑structure changes while preserving backward compatibility. Common strategies include:

URI versioning : embed the version in the URL path, e.g.

/api/v1/users

.

Request‑header versioning : specify the version in a header such as

Accept: application/vnd.company.app-v1+json

.

Query‑parameter versioning : pass the version as a query parameter, e.g.

/api/users?version=1

.

Content‑negotiation versioning : determine the version from the media type in the

Accept

header.

Spring 7.0 API Versioning

1745183810
1745183810

Spring Framework 7.0 adds native support for API versioning on the server side. By specifying a version range in the

@RequestMapping

annotation, requests are routed to different controller methods, simplifying multi‑version API management.

Implementation Approach

Developers can define the version range in

@RequestMapping

. Spring resolves the version from the request using one of several sources:

Request URL path : e.g.

/api/v1/users

or

/api/v2/users

.

Request‑header value : e.g.

Accept: application/vnd.company.app-v1+json

.

Other custom sources : developers can configure additional resolvers.

Example controller with two versioned endpoints:

<code>import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @RequestMapping(value = "/api/users", version = "1.0")
    public ResponseEntity<List<UserV1>> getUsersV1() {
        // version 1.0 implementation
        List<UserV1> users = fetchUsersV1();
        return ResponseEntity.ok(users);
    }

    @RequestMapping(value = "/api/users", version = "2.0")
    public ResponseEntity<List<UserV2>> getUsersV2() {
        // version 2.0 implementation
        List<UserV2> users = fetchUsersV2();
        return ResponseEntity.ok(users);
    }
}
</code>

In this example the

/api/users

endpoint dispatches to different methods based on the requested version (1.0 or 2.0), which can be supplied via URL path or request header.

Client‑side API Versioning

Spring also enhances client capabilities. Using

WebClient

or other tools, developers can set the desired API version in the request, ensuring the client talks to the correct version when multiple versions are deployed.

Client Example

Using

WebClient

to request version 1.0:

<code>import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class UserClient {
    private final WebClient webClient;

    public UserClient(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("http://example.com").build();
    }

    public Mono<List> getUsersV1() {
        return webClient.get()
                .uri("/api/users")
                .accept(MediaType.valueOf("application/vnd.company.app-v1+json"))
                .retrieve()
                .bodyToMono(List.class);
    }
}
</code>

The client sets the

Accept

header to

application/vnd.company.app-v1+json

, ensuring it receives the version 1.0 API.

Source Code Analysis of API Versioning

Spring’s implementation relies on several core classes.

ApiVersionResolver Interface

The functional interface defines how to extract a version from a request:

<code>@FunctionalInterface
public interface ApiVersionResolver {
    /**
     * Resolve the version from the given exchange.
     * @param exchange the current exchange
     * @return the version value, or null if not found
     */
    @Nullable
    String resolveVersion(ServerWebExchange exchange);
}
</code>

PathApiVersionResolver Implementation

This class extracts the version from a specific segment of the URL path:

<code>public class PathApiVersionResolver implements ApiVersionResolver {
    private final int pathSegmentIndex;

    public PathApiVersionResolver(int pathSegmentIndex) {
        Assert.isTrue(pathSegmentIndex >= 0, "'pathSegmentIndex' must be >= 0");
        this.pathSegmentIndex = pathSegmentIndex;
    }

    @Override
    @Nullable
    public String resolveVersion(ServerWebExchange exchange) {
        int i = 0;
        for (PathContainer.Element e : exchange.getRequest().getPath().pathWithinApplication().elements()) {
            if (e instanceof PathContainer.PathSegment && i++ == this.pathSegmentIndex) {
                return e.value();
            }
        }
        return null;
    }
}
</code>

For a path like

/api/v1/users

with

pathSegmentIndex = 1

, the resolver returns

v1

.

DefaultApiVersionStrategy Class

The default strategy handles parsing, comparing, and validating versions:

<code>public class DefaultApiVersionStrategy implements ApiVersionStrategy {
    @Override
    @Nullable
    public Comparable<?> parseVersion(ServerWebExchange exchange) {
        // implementation omitted for brevity
    }

    @Override
    @Nullable
    public Comparable<?> findDefaultVersion() {
        return this.defaultVersion;
    }

    @Override
    public boolean isVersionRequired() {
        return this.versionRequired;
    }

    @Override
    public void validateVersion(@Nullable Comparable<?> requestVersion, ServerWebExchange exchange) throws MissingApiVersionException, InvalidApiVersionException {
        // validation logic
    }
}
</code>

Version Request Condition

The

VersionRequestCondition

class integrates with Spring’s request‑mapping mechanism to route requests based on the resolved version:

<code>public class VersionRequestCondition implements RequestCondition<VersionRequestCondition> {
    @Override
    @Nullable
    public VersionRequestCondition getMatchingCondition(ServerWebExchange exchange) {
        Comparable<?> requestVersion = this.versionStrategy.parseVersion(exchange);
        return this;
    }
}
</code>

Conclusion

Spring Framework 7.0’s API versioning provides a flexible, extensible mechanism that lets developers manage API versions in a standardized way. Core components such as

ApiVersionResolver

,

ApiVersionStrategy

, and

VersionRequestCondition

work together to parse, validate, and route versioned requests, supporting common strategies while allowing custom extensions for specific business needs.

API versioningbackendJavaWebSpring
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.