Essentially, the annotation can be declared on any interface or abstract class and through the use of the abstract methods will be implemented for you at compile time, greatly simplifying the creation of HTTP clients.

    Let’s start with a simple example. Given the following class:

    Pet.java

    Pet.java

    1. class Pet {
    2. String name
    3. int age
    4. }

    Pet.java

    1. class Pet {
    2. var name: String? = null
    3. var age: Int = 0
    4. }

    You can define a common interface for saving new Pet instances:

    PetOperations.java

    1. import io.micronaut.http.annotation.Post;
    2. import io.micronaut.validation.Validated;
    3. import io.reactivex.Single;
    4. import javax.validation.constraints.Min;
    5. import javax.validation.constraints.NotBlank;
    6. @Validated
    7. public interface PetOperations {
    8. @Post
    9. Single<Pet> save(@NotBlank String name, @Min(1L) int age);
    10. }

    PetOperations.java

    PetOperations.java

    1. import io.micronaut.http.annotation.Post
    2. import io.micronaut.validation.Validated
    3. import io.reactivex.Single
    4. import javax.validation.constraints.Min
    5. import javax.validation.constraints.NotBlank
    6. @Validated
    7. @Post
    8. fun save(@NotBlank name: String, @Min(1L) age: Int): Single<Pet>
    9. }

    Note how the interface uses Micronaut’s HTTP annotations which are usable on both the server and client side. Also, as you can see you can use javax.validation constraints to validate arguments.

    Additionally, to use the javax.validation features you should have the validation module on your classpath:

    1. implementation("io.micronaut:micronaut-validator")
    1. <dependency>
    2. <groupId>io.micronaut</groupId>
    3. <artifactId>micronaut-validator</artifactId>
    4. </dependency>

    On the server-side of Micronaut you can implement the PetOperations interface:

    PetController.java

    PetController.java

    1. import io.micronaut.http.annotation.Controller
    2. import io.reactivex.Single
    3. @Controller("/pets")
    4. class PetController implements PetOperations {
    5. @Override
    6. Single<Pet> save(String name, int age) {
    7. Pet pet = new Pet()
    8. pet.setName(name)
    9. pet.setAge(age)
    10. // save to database or something
    11. return Single.just(pet)
    12. }
    13. }

    PetController.java

    1. import io.micronaut.http.annotation.Controller
    2. import io.reactivex.Single
    3. @Controller("/pets")
    4. open class PetController : PetOperations {
    5. override fun save(name: String, age: Int): Single<Pet> {
    6. val pet = Pet()
    7. pet.name = name
    8. pet.age = age
    9. // save to database or something
    10. return Single.just(pet)
    11. }

    PetClient.java

    1. import io.micronaut.http.client.annotation.Client;
    2. import io.reactivex.Single;
    3. @Client("/pets") (1)
    4. public interface PetClient extends PetOperations { (2)
    5. @Override
    6. Single<Pet> save(String name, int age); (3)
    7. }

    PetClient.java

    PetClient.java

    1. import io.micronaut.http.client.annotation.Client
    2. import io.reactivex.Single
    3. @Client("/pets") (1)
    4. interface PetClient : PetOperations { (2)
    5. override fun save(name: String, age: Int): Single<Pet> (3)
    6. }

    Once you have defined a client you can simply @Inject it wherever you may need it.

    Recall that the value of @Client can be:

    • An absolute URI. Example [https://api.twitter.com/1.1](https://api.twitter.com/1.1)

    • A relative URI, in which case the server targeted will be the current server (useful for testing)

    • A service identifier. See the section on Service Discovery for more information on this topic.

    In a production deployment you would typically use a service ID and to discover services automatically.

    Another important thing to notice regarding the save method in the example above is that it returns a Single type.

    This is a non-blocking reactive type and typically you want your HTTP clients not to block. There are cases where you may want to write an HTTP client that does block (such as in unit test cases), but this are rare.

    The following table illustrates common return types usable with :

    Generally, any reactive type that can be converted to the Publisher interface is supported as a return type including (but not limited to) the reactive types defined by RxJava 1.x, RxJava 2.x and Reactor 3.x.