6.18 API Versioning

Since 1.1.x, Micronaut supports API versioning via a dedicated @Version annotation.

The following example demonstrates how to version an API:

Versioning an API

  1. import io.micronaut.core.version.annotation.Version;
  2. import io.micronaut.http.annotation.Controller;
  3. import io.micronaut.http.annotation.Get;
  4. @Controller("/versioned")
  5. class VersionedController {
  6. @Version("1") (1)
  7. @Get("/hello")
  8. String helloV1() {
  9. return "helloV1";
  10. }
  11. @Version("2") (2)
  12. @Get("/hello")
  13. String helloV2() {
  14. return "helloV2";
  15. }

Versioning an API

  1. import io.micronaut.core.version.annotation.Version
  2. import io.micronaut.http.annotation.Controller
  3. import io.micronaut.http.annotation.Get
  4. @Controller("/versioned")
  5. class VersionedController {
  6. @Version("1") (1)
  7. @Get("/hello")
  8. String helloV1() {
  9. "helloV1"
  10. }
  11. @Version("2") (2)
  12. @Get("/hello")
  13. String helloV2() {
  14. "helloV2"
  15. }

Versioning an API

  1. import io.micronaut.core.version.annotation.Version
  2. import io.micronaut.http.annotation.Controller
  3. import io.micronaut.http.annotation.Get
  4. @Controller("/versioned")
  5. internal class VersionedController {
  6. @Version("1") (1)
  7. @Get("/hello")
  8. fun helloV1(): String {
  9. return "helloV1"
  10. }
  11. @Version("2") (2)
  12. @Get("/hello")
  13. fun helloV2(): String {
  14. return "helloV2"
  15. }
1The helloV1 method is declared as version 1
2The helloV2 method is declared as version 2

Then enable versioning by setting micronaut.router.versioning.enabled to true in application.yml:

Enabling Versioning

  1. micronaut:
  2. router:
  3. versioning:
  4. enabled: true

By default Micronaut has two strategies for resolving the version based on an HTTP header named X-API-VERSION or a request parameter named api-version, however this is configurable. A full configuration example can be seen below:

Configuring Versioning

  1. micronaut:
  2. router:
  3. versioning:
  4. enabled: true (1)
  5. parameter:
  6. enabled: false (2)
  7. names: 'v,api-version' (3)
  8. header:
  9. enabled: true (4)
  10. names: (5)
  11. - 'X-API-VERSION'
  12. - 'Accept-Version'
1Enables versioning
2Enables or disables parameter-based versioning
3Specify the parameter names as a comma-separated list
4Enables or disables header-based versioning
5Specify the header names as a list

If this is not enough you can also implement the RequestVersionResolver interface which receives the HttpRequest and can implement any strategy you choose.

Default Version

It is possible to supply a default version through configuration.

Configuring Default Version

  1. micronaut:
  2. router:
  3. versioning:
  4. enabled: true
  5. default-version: 3 (1)
1Sets the default version

A route is not matched if the following conditions are met:

  • The default version is configured

  • No version is found in the request

  • The route defines a version

  • The route version does not match the default version

If the incoming request specifies a version, the default version has no effect.

Versioning Client Requests

Micronaut’s Declarative HTTP client also supports automatic versioning of outgoing requests via the @Version annotation.

By default, if you annotate a client interface with @Version, the value supplied to the annotation is included using the X-API-VERSION header.

For example:

  1. import io.micronaut.core.version.annotation.Version;
  2. import io.micronaut.http.annotation.Get;
  3. import io.micronaut.http.client.annotation.Client;
  4. import org.reactivestreams.Publisher;
  5. import io.micronaut.core.async.annotation.SingleResult;
  6. @Client("/hello")
  7. @Version("1") (1)
  8. public interface HelloClient {
  9. @Get("/greeting/{name}")
  10. String sayHello(String name);
  11. @Version("2")
  12. @Get("/greeting/{name}")
  13. @SingleResult
  14. Publisher<String> sayHelloTwo(String name); (2)
  15. }
  1. import io.micronaut.core.version.annotation.Version
  2. import io.micronaut.http.annotation.Get
  3. import io.micronaut.http.client.annotation.Client
  4. import reactor.core.publisher.Mono
  5. @Client("/hello")
  6. @Version("1") (1)
  7. interface HelloClient {
  8. @Get("/greeting/{name}")
  9. String sayHello(String name)
  10. @Version("2")
  11. @Get("/greeting/{name}")
  12. Mono<String> sayHelloTwo(String name) (2)
  13. }
  1. import io.micronaut.core.version.annotation.Version
  2. import io.micronaut.http.annotation.Get
  3. import io.micronaut.http.client.annotation.Client
  4. import reactor.core.publisher.Mono
  5. @Client("/hello")
  6. @Version("1") (1)
  7. interface HelloClient {
  8. @Get("/greeting/{name}")
  9. fun sayHello(name : String) : String
  10. @Version("2")
  11. @Get("/greeting/{name}")
  12. fun sayHelloTwo(name : String) : Mono<String> (2)
  13. }
1The @Version annotation can be used at the type level to specify the version to use for all methods
2When defined at the method level it is used only for that method

The default behaviour for how the version is sent for each call can be configured with DefaultClientVersioningConfiguration:

🔗

Table 1. Configuration Properties for DefaultClientVersioningConfiguration
PropertyTypeDescription

micronaut.http.client.versioning.default.headers

java.util.List

The list of request header names.

micronaut.http.client.versioning.default.parameters

java.util.List

The list of request query parameter names.

For example to use Accept-Version as the header name:

Configuring Client Versioning

  1. micronaut:
  2. http:
  3. client:
  4. versioning:
  5. default:
  6. headers:
  7. - 'Accept-Version'
  8. - 'X-API-VERSION'

The default key refers to the default configuration. You can specify client-specific configuration by using the value passed to @Client (typically the service ID). For example:

Configuring Versioning

  1. micronaut:
  2. http:
  3. client:
  4. versioning:
  5. greeting-service:
  6. headers:
  7. - 'Accept-Version'
  8. - 'X-API-VERSION'

The above uses a key named greeting-service which can be used to configure a client annotated with @Client('greeting-service').