Asynchronous ways of routing in Kotlin with Spring Webflux

In this article I will show you two ways of routing endpoint and how to combine them with your preferred way of developing software.

WebFlux is the reactive-stack web framework of Spring. It was added later in version 5.0 is fully non-blocking, supports Reactive Streams back pressure, and runs on such servers as Netty, Undertow, and Servlet 3.1+ containers.

Kotlin is a cross-platform, statically typed, general-purpose programming language with type inference. Kotlin is designed to interoperate fully with Java, and the JVM version of Kotlin’s standard library depends on the Java Class Library, but type inference allows its syntax to be more concise.

Routing or router in web development is a mechanism where requests are routed to the code that handles them.

Declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.

Imperative programming is a programming paradigm that uses statements that change a program’s state.

Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that each return a value, rather than a sequence of imperative statements which change the state of the program.

Reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.

Coroutines are Kotlin lightweight threads allowing to write non-blocking code in an imperative way.

Routing and programming is a matter of choice, we can combine them as follows:

  • Annotated routing & coroutines (imperative programming)
  • Annotated routing & spring reactor (functional programming)
  • Declarative routing & coroutines (imperative programming)
  • Declarative routing & spring reactor (functional programming)

To start from a common ground for the 4 combinations we are given with a service class ReactiveNumberService.kt and we have to route incoming requests, consume that service and respond. To accomplish that we will create two handlers (one functional and one imperative) that will handle the communication between the router and the service.

Note: for simplicity of reading I will always put the returned types in every method even though I know that the compiler will infer it, and sometimes I will not use all of the kotlin syntactic sugar if I think that is more readable to a new kotlin developer

It is reactive which means that it will stream the results asynchronously, in a non blocking way so we have to program the router in async way to take advantage of it.

The service exposes two methods. The method echoNumber receives an integer and returns a flow of integer containing the input value after a delay of 1.5 seconds. The method generateRangeOfInts receives an integer and returns a flow of integer with the incremental value from 1 to the input number one by one every 100ms.

A flow is a cold asynchronous data stream that sequentially emits values and completes normally or with an exception.

The class is annotated with @Component so spring can inject it via the @Autowired annotation.

It isn’t the most imperative handler in the world, but the idea is that we can write imperative if we want to.

The methods are suspend(ed) because they can be suspended by the runtime to achieve asynchronizity.

Here the asynchronizity is achieved by returning reactive types such as Mono & Flux. Mono represents a 0..1 sequence and Flux represents a 0..N. Flow a kotlin equivalent to Flux.

For annotated routing we will use a combination of spring’s annotations like @RestController, @RequestMapping, @GetMapping, @PostMappin, @ResponseBody, etc.

To achieve asynchornizity we will use coroutines or spring reactor.

The annotated router that delegates into the coroutine handler

@RestController (introduced in Spring 4.0) simplifies the creation of RESTful web services. It’s a convenience annotation that combines @Controller and @ResponseBody . This eliminates the need to annotate every request handling method of the controller class with the @ResponseBody annotation.

@RequestMapping is used to map web requests to Spring Controller methods. At class level it servers as the first common part of the path.

@GetMapping is used to map get requests to methods. It defines a path and a produced content type.

The methods are suspend because they call coroutines and to do so it need to be suspended. Note: you can use not suspended methods but you will have to create a coroutineScope.

The annotated router that delegates into the reactive handler

For declarative routing spring provides the RouterFunctionDsl & CoRouterFunctionDsl that declares for example: accept, contentType, GET, POST and many more.

The declarative router that delegates into the coroutine handler

The declarative is easily readable: it accepts text/html content, listens to the path /dsl-coroutine and it handles two sub path.

The declarative router that delegates into the reactive handler

Conclusion

In this article I show you four choices: annotated/dsl and reactive/coroutine and its combinations. Now it’s a matter of personal taste which one to pick. There are not many pros & cons but the only thing that I like in declarative is that you can group all the routes of the application in a single class that do route and nothing more and with the annotated counterpart you have to read every method to see the same and you could have many controllers. But even this can be a con if you have more than one developer inserting routes and testing in certain environments.

Full source code

You can test all the routers cloning this repo https://github.com/josefigueredo/routing-kotlin and calling the shell scripts in the test section in the same way as I do in the demo.

Software developer