eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

In this tutorial, we’ll talk about how we can leverage the Micronaut Framework capabilities to implement evolving REST APIs.

In the ever-evolving landscape of software development projects, sometimes purely based on REST APIs, maintaining backward compatibility while introducing new features and improvements is a crucial challenge. One of the fundamental aspects of achieving this is that we must implement a technique called  API versioning.

We’ll explore the concept of API versioning in the context of Micronaut, a popular microservices framework for building efficient and scalable applications. We’ll delve into the importance of API versioning, different strategies to implement it in Micronaut, and best practices to ensure smooth version transitions.

2. Importance of API Versioning

API versioning is the practice of managing and evolving an application programming interface (API) to allow clients to continue using the existing version while also adopting newer versions when they are ready. It is essential for several reasons.

2.1. Maintaining Compatibility

As our application evolves, we may need to change our APIs to introduce new features, fix bugs, or improve performance. However, it’s also necessary to ensure that such changes do not disrupt existing clients. API versioning enables us to introduce changes while maintaining compatibility with previous versions.

2.2. Allowing Gradual Adoption

The clients of our APIs may have different timelines for adopting new versions. Therefore, providing multiple versions of our APIs allows clients to update their code with reasonable adoption time, reducing the risk of breaking their applications.

2.3. Facilitating Collaboration

It also facilitates collaboration between development teams. When different teams work on other parts of a system, or third-party developers integrate with our APIs, versioning allows each team to have a stable interface, even as changes are made elsewhere.

3. API Versioning Strategies in Micronaut

Micronaut offers different strategies for implementing API versioning. We aren’t going to discuss which one is the best one as it pretty much depends on the use case and the reality of the project. Nonetheless, we can discuss the specifics of each one of them.

3.1. URI Versioning

In the URI versioning, the version of the API is defined in the URI. This approach makes it clear which version of the API the client is consuming. Although the URL may not be as user-friendly as it can be, it clarifies to the client which version it uses.

@Controller("/v1/sheep/count")
public class SheepCountControllerV1 {

    @Get(
        uri = "{?max}",
        consumes = {"application/json"},
        produces = {"application/json"}
    )
    Flowable<String> countV1(@Nullable Integer max) {
        // implementation

Although it may not be practical, our clients are sure about the version used, which means transparency. From the development side, it’s easy to implement any business rules specific to a particular version, meaning a good level of isolation. However, one could argue it’s intrusive as the URI may change frequently. It may require hard coding from the client side and adds extra context not precisely specific to the resource.

3.2. Header Versioning

Another option to implement API versioning is to leverage the header to route the request to the right controller. Here is an example:

@Controller("/dog")
public class DogCountController {

    @Get(value = "/count", produces = {"application/json"})
    @Version("1")
    public Flowable<String> countV1(@QueryValue("max") @Nullable Integer max) {
        // logic
    }

    @Get(value = "/count", produces = {"application/json"})
    @Version("2")
    public Flowable<String> countV2(@QueryValue("max") @NonNull Integer max) {
        // logic  
    }
}

By simply using the @Version annotation, Microunat can redirect the request to the proper handler based on the header’s value. However, we still need to change some configurations, as we see next:

micronaut:
  router:
    versioning:
      enabled: true
      default-version: 2
      header:
        enabled: true
        names:
          - 'X-API-VERSION'

Now we just enabled versioning via Micronaut, defining version 2 as the default one in case no version is specified. The strategy used will be header-based, and the header X-API-VERSION will be used to determine the version. Actually, this is the default header Micronaut looks at, so in this case, there wouldn’t be a need to define it, but in the case we want to use another header, we could specify it like this.

Using headers, the URI remains clear and concise, we can preserve backward compatibility, the URI is purely resource-based, and it allows for more flexibility in the evolution of the API. However, it’s less intuitive and visible. The client has to be aware of the version he wants to use, and it’s a bit more error-prone. There is another similar strategy that consists of the use of MineTypes for this.

3.3. Parameter Versioning

This strategy leverages query parameters in the URI to do the routing. In terms of implementation in Mircronaut, it’s exactly like the previous strategy. We just need to add the @Version in our controllers. However, we need to change some properties:

micronaut:
  router:
    versioning:
      enabled: true
      default-version: 2
      parameter:
        enabled: true
        names: 'v,api-version'

With this, the client only needs to pass either v or api-version as query parameters in each request, and Micronat will take care of the routing for us.

When using this strategy once again, the URI will have no resource-related information, although less than having to change the URI itself. Besides that, the versioning is less explicit and more error-prone as well. This is not RESTful, and documentation is needed to avoid confusion. However, we can also appreciate the simplicity of the solution.

3.4. Custom Versioning

Micronaut also offers a custom way to implement API versioning where we can implement a versioning route resolver and show Micronaut which version to use. The implementation is simple, and we only need to implement an interface, like in the following example:

@Singleton
@Requires(property = "my.router.versioning.enabled", value = "true")
public class CustomVersionResolver implements RequestVersionResolver {

    @Inject
    @Value("${micronaut.router.versioning.default-version}")
    private String defaultVersion;

    @Override
    public Optional<String> resolve(HttpRequest<?> request) {
        var apiKey = Optional.ofNullable(request.getHeaders().get("api-key"));

        if (apiKey.isPresent() && !apiKey.get().isEmpty()) {
            return Optional.of(Integer.parseInt(apiKey.get())  % 2 == 0 ? "2" : "1");
        }

        return Optional.of(defaultVersion);
    }

}

Here, we can see how we can leverage any information in the request to implement a routing strategy, and Micronaut does the rest. This is powerful, but we need to be cautious because this may lead to poor and less intuitive forms of implementing versioning.

4. Conclusion

In this article, we saw how API versioning can be implemented using Micronaut. Moreover, we also discussed the different strategies existent for applying this technique and some of their nuances.

It’s also clear that selecting the right strategy involves weighing the importance of URI cleanliness, explicitness of versioning, ease of use, backward compatibility, RESTful adherence, and the specific needs of clients consuming the API. The optimal approach depends on our project’s unique requirements and constraints.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=REST)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)