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:

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);
    }
}

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:

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);
    }
}

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.

@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);
}

PathApiVersionResolver Implementation

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

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;
    }
}

DefaultApiVersionStrategy Class

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

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

VersionRequestCondition Class

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

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;
    }
}

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

API VersioningWeb 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

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.