Reactive REST with Quarkus made easy

Quarkus JAX RS implementation has improved a lot since its first release. Within this tutorial we will show some new features which are available in Quarkus starting from the new reactive REST paradigm.

Quarkus uses SmallRye Mutiny for as main Reactive library. In our first tutorial, we have discussed how to use Mutiny to deliver reactive Hibernate applications: Getting started with Hibernate reactive on Quarkus

We will now dig into Reactive REST Services, showing how you can style REST Services in a modern Quarkus application.

A new Reactive REST framework

The standard model used by REST applications is to use a single Thread for every request. This model, however, is not scalable as you will need one thread for every concurrent request, which places a limit on the achievable concurrency.

To rescue, the reactive execution model enables asynchronous execution and non-blocking IO.

RESTEasy Reactive is the next generation of HTTP framework. It can ensure the best throughput by handling each HTTP request on an IO thread. This model has the following benefits:

  • Firstly, smaller response time as there is no thread context switch.
  • Next, less memory and cpu usage as it decreases the usage of threads.
  • Finally, you are not limited by the number of threads.

Let’s see how to code a Reactive REST application from the grounds up.

Coding a Reactive applications

To start coding our application we can use Quarkus Generator or any other tool (Maven plugin, Quarkus CLI, etc). We need to include the following extension to our project:

This will add in your project, the following dependency:

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>

Next, let’s modify the default Endpoint class as follows:

@Path("/hello")
public class ReactiveGreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public  Uni<String> hello() {
    	return Uni.createFrom().item("hello");
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public Uni<String> greeting(String name) {
    	 return Uni.createFrom().item(name)
         .onItem().transform(n -> String.format("hello %s", name));
    }
 

    @GET
    @Produces(MediaType.SERVER_SENT_EVENTS)
    @RestSseElementType(MediaType.TEXT_PLAIN)
    @Path("/stream/{count}/{name}")
    public Multi<String> greetingsAsStream(int count, String name) {
    	 return Multi.createFrom().ticks().every(Duration.ofSeconds(1))
                 .onItem().transform(n -> String.format("hello %s - %d", name, n))
                 .transform().byTakingFirstItems(count);
    }


}

The main difference compared with classic REST services are Uni and Multi events. As discussed in our first tutorial, Mutiny offers two types that are both event-driven and lazy:

  • A Uni emits a single event (an item or a failure). A good example is the result of sending a message to a message broker queue.
  • A Multi emits multiple events (n items, 1 failure or 1 completion). A good example is receiving messages from a message broker queue.

Therefore, our Endpoint will produce the following events:

  • A Uni event which sends the text “hello” as event
  • A Uni event which publishes a text transformed from the incoming request parameter “name”
  • Finally, a Multi event which streams a set of greetings events transforming the incoming request parameter “name”
New Parameter Context injection

Did you notice our Endpoint does not declare any  @PathParam or @RestPath ? With RESTEasy Reactive you don’t need it as long as your parameter has the same name as a path parameter

Handling Server side events

In conclusion, to handle server side events, we will add a minimal AngularJS Module which will poll the REST Endpoint with some Form parameters. We will show just the main code snipped – check the source code for the full example:

<script type="text/javascript">
      var app = angular.module("RESTManagement", []);

      //Controller Part
      app.controller("RESTController", function ($scope, $http) {

         $scope.callrest = function () {

       
           var url =  "hello/stream/" + $scope.form.events + "/" + $scope.form.name;
           var eventSource = new EventSource(url);
           eventSource.onmessage = function (event) {
           var container = document.getElementById("divcontainer");
           var paragraph = document.createElement("p");
           paragraph.innerHTML = event.data;
           container.appendChild(paragraph);
           };

         }


      });
    </script>

Running the application

You can build and run the application as usual with:

mvn install quarkus:dev

Next, you can test the following endpoints:

  • /hello – returns “hello”
  • /greeting/name – returns a greeting for name
  • /streaming/name/count – returns a count of SSE with name

Moreover, the home page (index.html) captures the streaming events using AngularJS:

Adding per-class Exception Mapper

In the JAX-RS specification all exception mapping is done in a global manner. However, when using RESTEasy Reactive you can handle exceptions with a specific JAX-RS Resource Class through the annotation @ServerExceptionMapping:

@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/greeting/{name}")
public Uni<String> greeting(String name) {
   if (name == null || name.size().trim() == 0) {
       throw new IllegalArgumentException();
   } 
	 return Uni.createFrom().item(name)
     .onItem().transform(n -> String.format("hello %s", name));
}


@ServerExceptionMapper(IllegalArgumentException.class)
public Response handleIllegal() {
   return Response.status(409).build();
}

In the above example, the Exception mapper will be called only for exceptions thrown in the same class. If you want to intercept exceptions globally, simply define them outside of the Endpoint. For example:

class ExceptionMappers {
    @ServerExceptionMapper
    public RestResponse<String> mapException(IllegalArgumentException exc) {
        return RestResponse.status(Response.Status.CONFLICT, "Illegal argument!");
    }
}

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/jaxrs-reactive