How to create a reactive REST service using Spring WebFlux?

Reactive programming is gaining more popularity now than ever before.

Traditionally when you invoke a REST service it becomes a blocking call.

You need to wait until you get the response and then proceed with your next line of code.

With reactive programming , you can make an asynchronous call and then retrieve the response using call back functions whenever the response is available.

Spring provides this feature under Spring WebFlux technology.

You can now create REST Controllers which return response in an asynchronous way.

This post explains the basic steps of how to create such a REST service.

Let’s say you have a database where you store employee details.

You want to create REST services to retrieve employee details and to store employees into the database.

To achieve this you CANNOT use a tradition SQL database because for reactive programming to work the entire stack has to be reactive , from front end code to the REST service to the database layer.

Say you use Spring WebFlux , this makes the REST service asynchronous. But if you are connecting to a MySQL database the REST controller still need to wait for the response from the database. You loose the benefit of reactive programming in this case.

There are libraries like RxJava which can help you achieve reactive programming for traditional relational databases.But there is no native support provided by these databases.

So , let’s use a database which has the feature of reactively storing and sending data. Mongo DB is one and Spring provides a Reactive Repository to connect to MongoDB asynchronously.

In this example , we will use those features to create aysynchronouse/reactive REST services .

Below are the steps:

STEP 1: Create a Spring Boot project template

Go to https://start.spring.io/ .

Create a new project with the dependencies : Spring Web and Spring Data Reactive MongoDB.

STEP 2: Create a MongoDB database

Create a database named employeedb in your local mongo database.

To set up MongoDB ,create a database and store documents in it refer this post:

Creating a database in Mongo DB and performing CRUD operations

STEP 3: Start the database server

Keep your local Mongo DB server running by executing mongod.exe command as explained in the previous link

STEP 4: Configure Mongo DB

To configure Mongo DB create a configuration class which extends AbstractReactiveMongoConfiguration class.

Annotate this class with @Configuration and @EnableReactiveMongoRepositories annotations to indicate that the class is a configuration file and Spring needs to enable reactive mongo repositories respectively

Here is the class:

package com.reactive.webflux;

import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

@Configuration
@EnableReactiveMongoRepositories
public class MongoDBConfig extends AbstractReactiveMongoConfiguration {


    @Override
    protected String getDatabaseName() {
        return "employeedb";
    }


}

As you see only one of the methods getDatabaseName() needs to be implemented. Provide your database name here. Spring Boot , as opinionated as it is assumes that there is a Mongo DB instance running in your localhost on port 27017. If you need to explicitly mention the database URL override mongoClient() method provided by the class extended above:

 public @Bean MongoClient mongoClient() {
       return MongoClients.create("mongodb://localhost:27017");
   }

STEP 5: Create a Reactive Repository

Spring provides an interface named ReactiveMongoRepository to perform reactive database operations on MongoDB. Extend that interface:

package com.reactive.webflux;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

public interface MongoDBRepository extends ReactiveMongoRepository<Employee,String> {
}

Unlike the traditional repositories reactive repositories don’t return a list of data as such when you query for it. It wraps them in either Mono or Flux objects.

Mono is a wrapper for a single object.

Flux is a wrapper for multiple objects.

These objects can be subscribed for asynchronous data thereby providing the feature of reactive programming.

So to the above interface if you want to add a method to retrieve employees by age, you need to add the below method declaration:

    public Flux<Employee> findByAge(Integer age);

STEP 6: Create a domain object representing the database collection

Remember we created an employee collection in the employeedb MongoDB database.

To represent that collection create a POJO object annotated with @Document and the primary key field annotated with @Id :

package com.reactive.webflux;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.lang.annotation.Documented;


@Document
public class Employee {


    @Id
    private String id;

    private String name;

    private Integer age;

    private String technology;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getTechnology() {
        return technology;
    }

    public void setTechnology(String technology) {
        this.technology = technology;
    }
}

STEP 7: Create a REST Controller

Create a REST Controller class by annotating with @RestController just like traditional rest services.

All the GET and POST method definitions look the same except that GET methods return objects wrapped by Mono or Flux objects:

package com.reactive.webflux;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Map;

@RestController
public class ReactiveRestController {

    @Autowired
    private MongoDBRepository repository;


    @GetMapping("getEmployeeById")
    public Mono<Employee> getEmployeeById(@RequestBody Map<String, String> id) {


        System.out.println("Id" + id);
        return repository.findById(id.get("id"));
    }

    @GetMapping("/getAllEmployees")
    public Flux<Employee> getAllEmployees() {


        return repository.findAll();
    }

    @GetMapping("/getEmployeesByAge")
    public Flux<Employee> getEmployeesByAge(@RequestBody Map<String, String> request) {


        Integer age = Integer.parseInt(request.get("age"));
        return repository.findByAge(age);
    }

    @PostMapping("saveEmployee")
    public void saveEmployee(@RequestBody Employee employee) {


        repository.save(employee);
    }

}

As you see , /getAllEmployees returns not a list of Employees but a Flux object which wraps multiple objects .

Also /getEmployeeById returns a Mono object since it is a single employee object.

Let’s test the services ( I have already stored few employees in employeedb database)

Storing an employee:

Getting all employees:

Getting employees by age:

Getting Employee by Id (returns a Mono object)

Notice the response is the same as it would have been for a non reactive REST service.

Though the response returned is asynchronous, the REST client I have used (POST MAN) is a blocking client. So the response is shown after it is available.

To utilize the real benefit of reactive REST services , you need to consume them using clients which can handle asynchronous responses.

Spring provides WebClient module to handle this. It is an alternative to Spring Rest Template.

More on that in a later post.


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading