[Tuto] Building a Reactive RESTful API with Spring WebFlux Java

Antoine Cheron
10 min readMay 11, 2018

--

The year 2014 has seen the birth of the Reactive Manifesto which emphasizes the development of responsive, resilient, elastic and message driven systems. Since then, more than 22.000 people signed it and some major librairies were born to help developers build such reactive systems.

In the Java world first came RxJava and then Reactor, developed by Pivotal, the company behind Spring Framework. This is how Spring Framework 5.0.0 brought a whole new Reactive Stack beside the famous Servlet Stack, in Sept. 2017, named WebFlux.

“Spring WebFlux is a non-blocking web framework built from the ground up to take advantage of multi-core, next-generation processors and handle massive numbers of concurrent connections.” (spring.io)

As more and more clients demand them, it becomes important to be able to build reactive systems. And, let’s be honest, it’s a very exciting to build blazing fast systems and discover new technologies 😎. Moreover, reactive systems are great Cloud Native apps. This is exactly why the goal of this post is to guide you through building your first (maybe not) reactive RESTful API with Spring WebFlux Java.

Ready ?

What you will build

In this tutorial, you will build a RESTful CRUD API to manage users represented as the Person class. Your API will be able to list all the users it knows, retrieve a specific one, create, add, update and delete ones.

We will use a very simple model to represent a Person. It will have a first name, a family name, an age and an id.

A swagger for this API is available here, and the code of the resulting server on GitHub.

You will build your API following these 3 steps:

  1. Modeling the application domain
  2. Creating a first route /persons and instantiating a server that runs
  3. Creating the other routes

Spring WebFlux makes use of Reactor which provides the Mono and Flux classes. If you have never worked with them, I recommend you read the articles I wrote to start with Reactor from the ground up. The first one is [Reactor Java#1] How to create Mono and Flux ?

Project set up

We will use Java 8 and gradle as the dependency management and build tool. If not familiar with gradle, all you have to know is that the configuration of the build and the dependencies goes into the build.gradle file. Don’t worry, I’ll tell you each time you need to update this file.

Considering that you will add your Main class in the package com.yourname.reactivewebserver, your build.gradle should be the following.

group 'com.yourname'
version '0.0.1'
apply plugin: 'java'
apply plugin: 'application'
mainClassName = group + '.reactivewebserver.Main'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
// compile group: 'groupId', name: 'artifactId', version: 'v'
}

To test this set up you can create a Main class in the package com.yourname.reactivewebserver with a main function printing the famous Hello World! in the console. To run it, type ./gradlew run in a terminal.

Conventions

As functional programming fits well with asynchronous and concurrent programming we will write our code in a functional way. This is why I’ll use the Java 8 Optional and Stream interfaces. Also, data structures will be immutable.

It’s totally ok if you’re not familiar with this paradigm for this tutorial. If curious about discovering the basis of functional programming, you could start from here.

Modeling the application domain

The Person class described above is pretty straightforward. Add it into a domain package, so in com.yourname.reactivewebserver.domain .

public class Person {

private final String id;
private final String firstName;
private final String familyName;
private final int age;

public Person(String id, String firstName, String familyName, int age) {
this.id = id;
this.firstName = firstName;
this.familyName = familyName;
this.age = age;
}

public String getId() { return id; }
public String getFirstName() { return firstName; }
public String getFamilyName() { return familyName; }
public int getAge() { return age; }

}

As this class doesn’t have setters, Jackson, the most used JSON library in Java, won’t be able to create instances of your class. This is why we will have to write our own JSON parser for the Person class.

Json Reader and Writer

First, add a dependency to Jackson. To do it, open your build.gradle file and replace the empty dependencies {} block with:

dependencies {
compile group: 'io.projectreactor.ipc', name: 'reactor-netty', version: '0.7.6.RELEASE'
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.5'
}

Then, create a json package in com.yourname.reactivewebserver and add the following two classes into it. They will be used later as reader and writer.

Creating a first route

The first route you will expose will map to the operation of retrieving the list of persons the app holds. It will be associated to GET /persons and return a JSON document.

To create this first route, we already have our JSON writer. Now we need a service to retrieve the list of persons that we will name PersonService, another to deal with the HTTP layer, here PersonApi, and a Main to start our application.

Spring dependencies

Go back to the build.gradle file and add the following dependencies:

compile group: 'org.springframework', name: 'spring-webflux', version: '5.0.5.RELEASE'
compile group: 'org.springframework', name: 'spring-context', version: '5.0.5.RELEASE'

The main function

Let’s begin with the main function. Following the official documentation, our function is:

public static void main(String[] args) {
final PersonApi personApi = null; // TODO
final RouterFunction<ServerResponse> apiRoot =
RouterFunctions.nest(
RequestPredicates.path("/persons"),
personApi.routerFunction
);
final HttpHandler httpHandler = RouterFunctions.
toHttpHandler(apiRoot);
final ReactorHttpHandlerAdapter adapter =
new ReactorHttpHandlerAdapter(httpHandler);

HttpServer.create(8080).startAndAwait(adapter);
}

The above code instantiates the elements required by Spring Webflux. Do not focus on the PersonApi instance at the moment, we will create it in a few minutes. Instead, let’s focus on the RouterFunction element. RouterFunctions are elements that the Spring router will use to route requests to the proper function and return a 404 if the request doesn’t match any predicate.

In our case, if the user sends a request which path starts with /persons, Spring will pass the request to the personApi RouterFunction. Otherwise it will respond with a 404 status code.

Don’t worry if you don’t understand this part yet. We will write more RouterFunctions in the PersonApi component soon.

The PersonApi

The PersonApi is responsible for the HTTP layer of the server. On each request that matches a route that we will create, it will be responsible for verifying the correctness of the request, then executing the relevant operation on the data, handling the possible errors and then creating the proper response.

Let’s do exactly that for the GET /persons that will respond with the full list of persons the app holds.

package com.yourname.reactivewebserver.rest;public class PersonApi {
public final RouterFunction<ServerResponse> routerFunction;
private final PersonService personService;
public PersonApi(PersonService personService) {
this.personService = personService;
this.routerFunction = RouterFunctions.
route(RequestPredicates.GET(""), this::getAllPersons);
}
private Mono<ServerResponse> getAllPersons(ServerRequest request){
return this.personService.list() // returns a Flux<Person>
.collectList()
// turns the flux into a Mono<List<T>> to allow sending a single response
.flatMap(JsonWriter::write)
.flatMap((json) -> ServerResponse.ok()
.body(Mono.just(json), String.class)
).onErrorResume(
JsonProcessingException.class,
(e) -> ServerResponse.status(INTERNAL_SERVER_ERROR)
.body(Mono.just(e.getMessage()), String.class)
);
}
}

We won’t instantiate it in the main right away. First we will create the PersonService class.

The PersonService

In this tutorial, our PersonService will not persist the data into a database. Instead, it will store the data in-memory.

Now, let’s first create the list() method that we used in PersonApi. It will return aFlux<Person> containing all the Person instances the app holds.

The code for our PersonService is the following:

package com.yourname.reactivewebserver.services;public class PersonService {
private final List<Person> persons;
public PersonService() {
this.persons = Stream.of(
// Create new instance of Person here, as many as you wish
new Person("id", "foo", "bar", 1)
).collect(Collectors.toCollection(CopyOnWriteArrayList::new));
}
public Flux<Person> list() {
return Flux.fromIterable(this.persons);
}
}

Time to test it !!

Yes, as the title suggest, it is time to start your server for the first time.

Go back to the main function and replace final PersonApi personApi = null; // TODO with final PersonApi personApi = new PersonApi(new PersonService());

Now you can ./gradlew run in your terminal :). Open Postman, or Insomnia and make a GET request on http://localhost:8080/persons . You should see the below response.

Sample response from http://localhost:8080/persons

Is my server really reactive ?

Now it is time to make sure our server really is reactive, and, spoiler alert… NO it’s not, not yet.

To observe it, in the PersonApi.getAllPersons(…) method, replace

.flatMap((json) ->
ServerResponse.ok().body(Mono.just(json), String.class))

with

.flatMap((json) -> {
System.out.println("Executing on thread: " +
Thread.currentThread().getName()
);
ServerResponse.ok().body(Mono.just(json), String.class)
})

Then, make a few requests on the server. You will observe that there are as many threads handling the requests as your computer’s CPU has. On my computer, it results running on 4 threads. Their name is reactor-http-nio-N.

On one side, this is still 4 times better than single-threaded technologies. On the other side, we can make it much better.

A big improve in performances

In order to make the server use as many threads as it needs to, we will have to control the execution of the Mono we use.

This can be done with Schedulers , which are components provided by Reactor that are some highly configurable Thread Pools, coming with 4 pre-configured Schedulers: immediate, single, parallel and elastic.

For more information on Schedulers, please refer to the dedicated page of the official documentation here.

In our case, we will use the elastic scheduler as it is the most efficient in most cases. You could prefer the parallel scheduler if doing very intensive computing on the server. If this is the case, I suppose you already know what scheduling technique best fits your use case.

A word about the elastic scheduler. As stated in the official documentation, “it creates new worker pools as needed, and reuse idle ones. Worker pools that stay idle for too long (default is 60s) are disposed. This is a good choice for I/O blocking work for instance”.

To configure the scheduler a Mono, or Flux, should use, call its subscribeOn(Scheduler s) method. You can see this method as a configuration method. If calling it several times, the Mono will subscribe on the first scheduler you (or the library’s author) passed it.

Now comes the question: where should we configure the scheduler to use ? The answer is: both in the service methods and the API methods. The one configured in the service method will allow fine tuning service execution, mostly dealing with I/O delays. This will be the scheduler used for the success path. And the one in the API will be set for the error path and request handler method that do not make use of the service.

Now, create a Config class and add a public static final Scheduler SCHEDULER = Schedulers.elastic(); attribute into it. Then, add .subscribeOn(Config.SCHEDULER) at the end of PersonService.list() and PersonApi.getAllPersons(…) .

There we are, our server became able to handle a lot more requests.

Creating the other routes

To help us handling errors I created a few Exception classes that you can find here. I will use them often in the rest of this post. In the meantime, you can take a look at the utils package I’ve done.

In this section, I will detail the most complicated case that is the creation of the POST /persons/{id} route. This route will be mapped to an updateOnePersonById method of the PersonService. We’ll start with this method. Then your goal will be to build the other routes by yourself.

Person Service

public Mono<Boolean> updateOneById(Person person) {
return this.findById(person.getId()).
map((previous) ->
this.persons.remove(previous) && this.persons.add(person)
).
subscribeOn(Config.APPLICATION_SCHEDULER);
}

PersonApi method

The biggest piece comes in the PersonApi class. Let’s first write an updateOnePersonById method to handle the request. This method is :

private Mono<ServerResponse> updateOnePersonById(ServerRequest request) {
return request.bodyToMono(String.class).
flatMap(this::readPersonFromRequestBody).
filter((person) ->
person.getId().equals(
request.pathVariable(PERSON_ID_PATH_VARIABLE)
)
).
flatMap(this.personService::updateOneById).
flatMap((success) -> success ?
Responses.noContent() : Responses.internalServerError()
).
onErrorResume(
NotFoundResourceException.class,
Responses::notFound
).
onErrorResume(
InvalidRequestBodyException.class,
Responses::badRequest
).
switchIfEmpty(Responses.badRequest()).
subscribeOn(Config.APPLICATION_SCHEDULER);
}
private Mono<Person> readPersonFromRequestBody(String body) {
return MonoUtils.fromOptional(
PersonReader.read(body),
() -> new InvalidRequestBodyException(Person.class)
);
}

The method does the following:

  1. Read the request body as a String , that actually is a JSON document.
  2. Try to parse it and instantiate a Person instance. If impossible, the Mono becomes a MonoError<> holding an InvalidRequestBodyException .
  3. Verify that the id in the path and the id of the Person object sent in the request are consistent, because ids shouldn’t be changed.
  4. Invoque the PersonService.updateOneById method to make the operation.
  5. Handle the probably false value returned by the service to tell the user she made that an internal server error occurred.
  6. In case a NotFoundResourceException was thrown before, returns a 404 Not Found response.
  7. In case a InvalidRequestBodyException was thrown before, returns a 400 Bad Request response.
  8. Returns a 400 Bad Request response in case the Mono is empty, which means that the id in the path and the one in the body are different.
  9. Apply the configured scheduler.

As you might have noticed, the goal in writing this code is to maximize the proportion of business logic as the Api is an interface. It is good practice to avoid writing implementation details code in your Api classes.

PersonApi routerFunction

Last, we have to add the route to the PersonApi routerFunction. Here, we need the andRoute(predicate, handler) method. This component becomes:

this.routerFunction = RouterFunctions.
route(GET(""), this::getAllPersons).
andRoute(
POST("/{id}").and(contentType(APPLICATION_JSON)),
this::getOnePersonById
);

There we are !!

“Three pineapples wearing party hats, and one wearing sunglasses, surrounded by colourful balloons and party items, by Scott Webb Photography” by Pineapple Supply Co. on Unsplash

Yes now we’re done with our second route. Restart the webserver and update the foo bar person with an HTTP request on /persons/{id} 😉.

Now, you can implement the other routes by yourself. You can always refer to my implementation on Github.

Congratulations !

Here we are, you built your first reactive webserver in Java with Spring WebFlux.

Thanks for reading. I hope this post helped you, and that you liked it. Don’t hesitate to write a comment below to help me improving it. Otherwise, any kind message would be greatly appreciated.

Have fun coding ! 🤘

--

--