Backend Development 11 min read

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

This article explains why API version control is essential for modern web services, outlines common versioning strategies such as URI, header, query‑parameter and content negotiation, and demonstrates how Spring Framework 7.0 natively supports versioned routing on both server and client sides with practical code examples.

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

Importance of API Version Control

API version control lets developers introduce new features, fix bugs, or change data structures while preserving backward compatibility. Common strategies include:

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

/api/v1/users

.

Header versioning : specify the version via a request 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 : determine the version based on the media type in the

Accept

header.

Spring 7.0 API Version Control

Spring Framework 7.0 adds native support for API versioning. By adding a

version

attribute to the

@RequestMapping

annotation, requests are routed to the appropriate controller method based on the requested version.

Implementation Details

Developers can specify a version range in

@RequestMapping

. Spring resolves the version from the request and dispatches to the matching method. Versions can be parsed from:

Request URL path : e.g.,

/api/v1/users

or

/api/v2/users

.

Request header : e.g.,

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

.

Custom sources : developers may configure additional resolvers.

Example controller demonstrating two API versions:

<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() {
        // implementation for version 1.0
        List<UserV1> users = fetchUsersV1();
        return ResponseEntity.ok(users);
    }

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

In this example the

/api/users

endpoint calls different methods depending on whether the request specifies version

1.0

or

2.0

. The version can be supplied via the URL path (e.g.,

/api/v1/users

) or a request header.

Client API Version Control

Spring 7.0 also enhances client‑side versioning. When using

WebClient

or similar tools, developers can set the desired API version in the request headers to ensure the client interacts with the correct version.

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<User>> 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

, guaranteeing that the server returns data for API version 1.0.

API Version Control Source Analysis

Spring Framework 7.0 implements API versioning through several core classes:

ApiVersionResolver Interface

This functional interface defines a contract for extracting a version string 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

Extracts the version from a specific segment of the URL path.

<code>public class PathApiVersionResolver implements ApiVersionResolver {

    private final int pathSegmentIndex;

    /**
     * Create a resolver instance.
     * @param pathSegmentIndex the index of the path segment containing the API version
     */
    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>

DefaultApiVersionStrategy Class

Provides the default strategy for parsing, validating, and supplying a default version.

<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 {
        // implementation omitted for brevity
    }
}
</code>

VersionRequestCondition Class

Acts as a request‑mapping condition that matches incoming requests to the appropriate versioned handler.

<code>public class VersionRequestCondition implements RequestCondition<VersionRequestCondition> {

    @Override
    @Nullable
    public VersionRequestCondition getMatchingCondition(ServerWebExchange exchange) {
        Comparable<?> requestVersion = this.versionStrategy.parseVersion(exchange);
        // matching logic omitted for brevity
        return this;
    }
}
</code>

Conclusion

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

ApiVersionResolver

,

DefaultApiVersionStrategy

, and

VersionRequestCondition

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

API versioningbackendJavaSpringWeb Development
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.