This article will teach us how to build reactive microservices with Spring WebFlux using a related example. Here, we create a student microservice to handle students and their data. For understanding purposes, we provide basic features and functionalities such as adding a student, searching for a student, deleting a student, and getting all student data.
Student Microservice Functionalities
- Add Student: For adding new student data we need to provide the name and age of the student.
- Delete Student: If you want to delete a student you need a student ID then only you can delete otherwise you will get a Not Student ID found.
- Search Student Data: If you want to search a student's details you need a student ID then only you can search student data otherwise you will get No Student ID found.
- Get All Students data: We can get all the existing student data.
Prerequisites:
- Spring Framework
- Spring WebFlux
- Error Handling in Spring WebFlux
- Spring Annotations
- Components & Beans in Spring Framework
- Java Programming
Tools & Technologies:
- Spring Tool Suite
- Spring Framework
- Reactive programming
- Maven
- MongoDB
Steps to Build Reactive Microservice with Spring WebFlux
Here, we created one simple spring reactive project by using spring initializr. Below, we have provided the dependencies which are used in this Spring Application.
Project dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Project Folder Structure:

Step 1: Create a Spring Starter Project
Once Project creation is completed then connect with the database. Here, we use MongoDB and below we have provided the connection details.
Database Connection:
spring.application.name=microservices
spring.data.mongodb.host=localhost
spring.data.mongodb.database=studentdb
spring.data.mongodb.port=27017
Step 2: Create Entity Class
After that, we created a Student Entity class which is used for data handling while performing the database related operations.
- Here, we use lombok dependency for setters and getters and constructors creation.
- After this, we define the collection name by using @document annotation.
- Then we define the required student fields.
Student Data:
- Id
- Student Name
- Student Age
Student.java:
package com.app;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "students")
public class Student {
@Id
private String id;
private String name;
private String age;
}
Step 3: Create Repository
Here, we created a Repository class by using @Repository annotation by using a interface named StudentRepo and it is extends to ReactiveMongoRepository.
This will take entity class and data type of that entity class as input arguments. This repository is used for performing database related operations in Student microservice.
StudentRepo.java:
package com.app;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudentRepo extends ReactiveMongoRepository<Student, String>{
}
Step 4: Create Service Class
Here, we created a student handler by using @Service annotation.
- After that, we autowired required classes by using @Autowired annotation.
- Here, we write required logic for add student, delete student, search student details and get all student data by using Mono publisher help.
Below we provide the java code for your reference.
StudentHandler.java:
package com.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Service
public class StudentHandler {
@Autowired
private StudentRepo repo;
public Mono<ServerResponse> saveStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.save(data).flatMap(done -> {
return ServerResponse.ok().bodyValue("Student Saved Successfully \n" + data);
});
});
}
public Mono<ServerResponse> findStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.findById(data.getId()).flatMap(done -> {
return ServerResponse.ok().bodyValue(done);
});
}).onErrorResume(e -> {
return ServerResponse.badRequest().bodyValue("Student ID Not Found");
});
}
public Mono<ServerResponse> findAll(ServerRequest request) {
return repo.findAll().collectList().flatMap(done -> ServerResponse.ok().bodyValue(done))
.onErrorResume(e -> ServerResponse.badRequest().bodyValue("No Data Found"));
}
public Mono<ServerResponse> deleteStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.deleteById(data.getId()).then(ServerResponse.ok().bodyValue("Student Details Deleted"))
.onErrorResume(e -> ServerResponse.badRequest().bodyValue("Student ID Not Found"));
});
}
}
Step 5: Create Router Function
Here, we created RouterFunction which is used for define the API endpoints.
RouterConfig.java:
package com.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class RouterConfig {
@Autowired
private StudentHandler handler;
@Bean
RouterFunction<ServerResponse> router(){
return RouterFunctions.route(RequestPredicates.POST("/save"), handler::saveStudent)
.andRoute(RequestPredicates.POST("/search"), handler::findStudent)
.andRoute(RequestPredicates.POST("/all"), handler::findAll)
.andRoute(RequestPredicates.POST("/delete"), handler::deleteStudent)
;
}
}
Step 6: Run the Application
Once development is completed, then run this project as Spring Boot App. By default, the project is running on port number 8080 with Netty server.

APIs Information
Once project successfully running, then test the APIs. Here we use Postman tool for API testing.
Save Student API:
http://localhost:8080/save
This API is used for save the student student details by using post mapping.
public Mono<ServerResponse> saveStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.save(data).flatMap(done -> {
return ServerResponse.ok().bodyValue("Student Saved Successfully \n" + data);
});
});
}Output:

Search Student API:
http://localhost:8080/searchThis API is used for search student details by using Student id. Otherwise, it will throw Not Found.
public Mono<ServerResponse> findStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.findById(data.getId()).flatMap(done -> {
return ServerResponse.ok().bodyValue(done);
});
}).onErrorResume(e -> {
return ServerResponse.badRequest().bodyValue("Student ID Not Found");
});
}Output:

Get All API:
This API is used for display the all existing data in the database.
http://localhost:8080/allpublic Mono<ServerResponse> findAll(ServerRequest request) {
return repo.findAll().collectList().flatMap(done -> ServerResponse.ok().bodyValue(done))
.onErrorResume(e -> ServerResponse.badRequest().bodyValue("No Data Found"));
}Output:

Delete Student API:
This API is used for delete a existing student data with help of Student ID.
http://localhost:8080/deletepublic Mono<ServerResponse> deleteStudent(ServerRequest request) {
return request.bodyToMono(Student.class).flatMap(data -> {
return repo.deleteById(data.getId()).then(ServerResponse.ok().bodyValue("Student Details Deleted"))
.onErrorResume(e -> ServerResponse.badRequest().bodyValue("Student ID Not Found"));
});
}Output:
