Spring Boot Webflux: The functional approach

Reading Time: 3 minutes

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.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Let’s create a model class for User.

User.java

@Getter
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Builder(toBuilder = true)
@AllArgsConstructor
@ToString
@Table("user")
public class User {
    @PrimaryKey
    int id;
    String name;
    String email;

}

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

UserRouter.java

@Configuration
public class UserRouter {
   @Bean
    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))
                .build();
    }
}

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.

UserHandler.java

@Component
public class UserHandler {
    private final UserRepository userRepository;
    @Inject
    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)))
                .switchIfEmpty(notFound().build());
    }
    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:

UserRepository.java

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.

application.properties

cassandra.contactpoint.one = ${CAS_CONTACT_POINT_ONE:127.0.0.1}
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.

UserApplication.java

@SpringBootApplication
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!

References:
Spring Official Doc on Webflux

Knoldus-Scala-Spark-Services

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

1 thought on “Spring Boot Webflux: The functional approach4 min read

Comments are closed.

Knoldus Pune Careers - Hiring Freshers

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