Spring Boot Webflux: The functional approach

With the world moving towards the reactive and functional ways of programming, Spring also turned its steps on the same path. This leads to the origin of Spring Webflux. To understand the functional approach towards it, we first need to understand, what is Spring Webflux exactly?

Before Spring version 5.0, the original web framework included in the Spring Framework, Spring Web MVC, was built for the Servlet API and Servlet containers. This imperative style of programming was easy. But, this was blocking. With Spring 5.0, there was a new reactive-stack web framework introduced, known as Spring Webflux. It does not require the Servlet API, is fully asynchronous and non-blocking, and implements the Reactive Streams specification through the Reactor project.

Spring WebFlux comes in two flavors: functional and annotation-based. The focus of this blog will be towards the functional approach. Let’s try to understand it with the help of a User application.

To add Spring Webflux, add the following dependency in your spring project pom.xml.


Let’s create a model class for User.


@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Builder(toBuilder = true)
public class User {
    int id;
    String name;
    String email;


Now, we have to define UserRouter class to list our routes for the application.


public class UserRouter {
    public RouterFunction<ServerResponse> userRoutes(UserHandler userHandler) {
        return RouterFunctions.route()
                .path("/users", builder -> builder
                        .POST("", accept(MediaType.APPLICATION_JSON), userHandler::createUser)
                        .GET("/{id}", accept(MediaType.APPLICATION_JSON), userHandler::getUser)
                        .GET("", accept(MediaType.APPLICATION_JSON), userHandler::getUsers))

Router functions are evaluated in order: if the first route does not match, the second is evaluated, and so on. Therefore, it makes sense to declare more specific routes before general ones. Note that this behavior is different from the annotation-based programming model, where the “most specific” controller method is picked automatically.

Our router needs to have a handler defined to perform the requested functionality. Now, we need to create a UserHandler for handling the incoming requests.


public class UserHandler {
    private final UserRepository userRepository;
    public UserHandler(UserRepository userRepository) {
        this.userRepository = userRepository;
    public Mono<ServerResponse> createUser(ServerRequest request) {
        Mono<User> user = request.bodyToMono(User.class);
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(fromPublisher(user.flatMap(userRepository::save), User.class));
    public Mono<ServerResponse> getUser(ServerRequest request) {
        final int userId = Integer.parseInt(request.pathVariable("userId"));
        final Mono<User> user = userRepository.findById();
        return user.flatMap(usr -> ok().contentType(APPLICATION_JSON)
                .body(fromPublisher(user, User.class)))
    public Mono<ServerResponse> getUsers(ServerRequest request) {
        return ok().contentType(APPLICATION_JSON)
                .body(fromPublisher(userRepository::findAll, User.class));

ServerRequest and ServerResponse are immutable interfaces that offer JDK 8-friendly access to the HTTP request and response. Both request and response provide Reactive Streams back pressure against the body streams. The request body is represented with a Reactor Flux or Mono. The response body is represented with any Reactive Streams Publisher, including Flux and Mono.

The UserRepository interface can be defined in the following ways:


public interface UserRepository extends ReactiveCrudRepository<User, Integer> {
    Flux<User> findAll();
    Mono<User> findById(int id);
    Mono<User> save(User user);

Here, our UserRepository is extending ReactiveCrudRepository which follows the repositories programming model. The repositories programming model is the most high-level abstraction Spring Data users usually deal with. They’re usually comprised of a set of CRUD methods defined in a Spring Data provided an interface and domain-specific query methods. It belongs to “org.springframework.data.repository.reactive”. I am here using Cassandra as a database for my application. The DB configurations are not needed to be defined separately. We can have them served in the application.properties.


cassandra.contactpoint.one = ${CAS_CONTACT_POINT_ONE:}
spring.data.cassandra.contact-points = ${cassandra.contactpoint.one}
spring.data.cassandra.port = ${CAS_CONTACT_POINTS_PORT:9042}
spring.data.cassandra.keyspace-name = ${CASSANDRA_KEYSPACE:user_test}

You can define your UserApplication class in the following ways. This will be the starting point of your application.


public class UsersApplication {
    public static void main(String[] args) {
        SpringApplication.run(UsersApplication.class, args);

You can run your new service by executing the following command from the root directory of your project:

mvn spring-boot:run

The entire code for the above user application can be found at this GITHUB repository.

That’s all for this topic Spring Webflux: The functional approach. If you have any doubt or any suggestions to make please drop a comment. Thanks!

Spring Official Doc on Webflux


Written by 

Vinisha Sharma is a software consultant having more than 6 months of experience. She thrives in a fast pace environment and loves exploring new technologies. She has experience with the languages such as C, C++, Java, Scala and is currently working on Java 8. Her hobbies include sketching and dancing. She believes Optimism and a learning attitude is the key to achieve success in your life

Knoldus Pune Careers - Hiring Freshers

Get a head start on your career at Knoldus. Join us!