Building Reactive Rest CRUD APIs Using Spring Boot, WebFlux, and MongoDB

Hello everyone, today we will learn how to use Spring Boot, Spring WebFlux, and Spring Data to create a reactive web service that interacts with MongoDB.





Reactive APIs are non-blocking and tend to be more efficient because they’re not tying up processing while waiting for stuff to happen. Reactive systems adopt asynchronous I/O. Reactive apps allow us to scale better if we are dealing with lots of streaming data. 
If we are going to build a reactive app, we need it to be reactive all the way down to your database. Use a blocking JDBC driver with Spring WebFlux, and we will be displeased with its performance. Use a reactive NoSQL database like Cassandra, MongoDB, Couchbase, and Redis – and we will be satisfied with its performance.
Spring WebFlux uses a library called Reactor for its reactive support. The Reactor is an implementation of the Reactive Streams specification. The Reactor Provides two main types called Flux and Mono. Both of these types implement the Publisher interface provided by Reactive Streams. Flux is used to represent a stream of 0..N elements and Mono is used to represent a stream of 0..1 element.

Let’s build a Reactive Restful Service in Spring Boot

Now we are going to build a Restful API for a User CRUD application. We’ll use MongoDB as our data store along with the reactive MongoDB driver. We’ll build REST APIs for creating, retrieving, updating, and deleting user details. All the REST APIs will be asynchronous and will return a Publisher.

Project Directory




Maven[pom.xml]

Puts spring-boot-starter-data-mongodb-reactivespring-boot-starter-webflux dependencies.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.knf.sibin.dev</groupId>
<artifactId>springboot-webflux-reactivemonogo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-webflux-reactivemonogo</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>17</java.version>
</properties>

<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.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>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>



application.properties

spring.data.mongodb.uri=mongodb://localhost:27017/users



Define  a Modal Class for MongoDB

@Document(collection = "users")
public class User {

@Id
private String id = UUID.randomUUID()
.toString().substring(0, 10);
private String name;
private String emailId;

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 String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public User(String id, String name, String emailId) {
super();
this.id = id;
this.name = name;
this.emailId = emailId;
}

public User() {
super();
}
}

@Document is an annotation provided by the Spring Data project. It is used to identify a domain object, which is persisted to MongoDB. So you can use it to map a Java class into a collection inside MongoDB.

A property or field annotated with @Id (org.springframework.data.annotation.Id) maps to the _id field.



Create a Reactive Mongo Repository 

public interface UserRepository 
extends ReactiveMongoRepository<User, String> {

}
By extending from the ReactiveMongoRepository, we have a reactive MongoDB repository.



Create User Service and User Service Implementation
public interface UserService {

Mono<User> save(User user);

Mono<User> delete(String id);

Mono<User> update(String id, User user);

Flux<User> findAll();

Mono<User> findById(String id);
}



Create User Service Implementation

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

@Override
public Mono<User> save(User user) {
return this.userRepository.save(user);
}

@Override
public Mono<User> delete(String id) {

return this.userRepository
.findById(id).flatMap(p ->
this.userRepository
.deleteById(p.getId())
.thenReturn(p));

}

@Override
public Mono<User> update(String id, User user) {

return this.userRepository.findById(id)
.flatMap(u -> {
u.setId(id);
u.setEmailId(user.getEmailId());
u.setName(user.getName());
return save(u);
}).switchIfEmpty(Mono.empty());
}

@Override
public Flux<User> findAll() {
return this.userRepository.findAll();
}

@Override
public Mono<User> findById(String id) {
return this.userRepository.findById(id);
}
}
The @Service annotation represents that our bean holds some business logic.



Implement a Reactive Rest Controller with Spring WebFlux
@RestController
public class UserController {

@Autowired
private UserService userService;

@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
private Mono<User> save(@RequestBody User user) {

return this.userService.save(user);
}

@DeleteMapping("/users/{id}")
private Mono<ResponseEntity<String>> delete
(@PathVariable("id") String id) {

return this.userService.delete(id)
.flatMap(user -> Mono.just(ResponseEntity
.ok("Deleted Successfully")))
.switchIfEmpty(Mono.just(ResponseEntity
.notFound().build()));

}

@PutMapping("/users/{id}")
private Mono<ResponseEntity<User>> update
(@PathVariable("id") String id,
@RequestBody User user) {

return this.userService.update(id, user)
.flatMap(user1 -> Mono.just(ResponseEntity
.ok(user1))).switchIfEmpty(Mono
.just(ResponseEntity.notFound().build()));

}

@GetMapping(value = "/users")
private Flux<User> findAll() {

return this.userService.findAll();
}
}
The @RestController annotation was introduced in Spring 4.0 to simplify the engendering of RESTful web services. It's a convenience annotation that combines @Controller and @ResponseBody.



Spring Boot Main Driver
@SpringBootApplication
@EnableReactiveMongoRepositories
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
The Spring Boot application's main class contains a public static void main() method that starts up the Spring ApplicationContext.


Download the complete source code - click here                                     

Local Setup and Run the application

Step1: Download or clone the source code from GitHub to a local machine - Click here


Step 2: mvn clean install


Step 3: Run the Spring Boot application - mvn spring-boot:run


Test the endpoints using postman

1. Create a user:



2. Update user:



3. Fetch all users:



4. Delete user:

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete