Reactive Rest CRUD APIs using Spring Boot, WebFlux, and Reactive Mongo

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 displeasure in 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.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.sibin.dev</groupId>
<artifactId>knf_springboot_webflux_reactivemonogo_reactiveapi_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>knf_springboot_webflux_reactivemonogo_reactiveapi_demo</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</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>

Define a Model Class for MongoDB

Create User.java Class

package com.knf.reactivestack.dev.domain;

import java.util.UUID;

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

@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;
}

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

/**
*
*/
public User() {
super();
}
}

Add a Reactive Monto Repository class [UserRepository.class]

package com.knf.reactivestack.dev.repository;

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

import com.knf.reactivestack.dev.domain.User;

public interface UserRepository extends ReactiveMongoRepository<User, String> {

}

Add User Service and User Service Implementation

[UserService.java]

package com.knf.reactivestack.dev.service;

import com.knf.reactivestack.dev.domain.User;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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);
}

[UserServiceImpl.java]

package com.knf.reactivestack.dev.service.impl;

import com.knf.reactivestack.dev.domain.User;
import com.knf.reactivestack.dev.repository.UserRepository;
import com.knf.reactivestack.dev.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@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(user1 -> {
user.setId(id);
return save(user);
}).switchIfEmpty(Mono.empty());
}

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

@Override
public Mono<User> findById(String id) {
return this.userRepository.findById(id);
}
}

Implement a Controller with Spring WebFluxclass

package com.knf.reactivestack.dev.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.knf.reactivestack.dev.domain.User;
import com.knf.reactivestack.dev.service.UserService;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@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();
}

}

Driver Class 

package com.knf.reactivestack.dev;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.
                                                EnableReactiveMongoRepositories;

@SpringBootApplication
@EnableReactiveMongoRepositories
public class MainApplication {

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

}

application.properties

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

Run:$ mvn spring-boot:run 

Test the endpoints using postman


1)create user



2)update user


3)fetch all user


4)delete user

Popular posts from this blog

Spring boot video streaming example-HTML5

DataTable-Pagination example with Spring boot, jQuery and ajax

10 Best Job Posting Sites 2021-2022

Spring boot web project free download:User Registration System

Spring Boot + JPA/Hibernate One to Many mapping example

5 Hardest Puzzle,100% fail answers

Java security AES,SHA256,SHA512,MD5-Spring Boot Project Free Download

Spring Boot-AngularJS-Bootstrap-JPA-CRUD

ReactJS - Bootstrap - Buttons

Spring Boot file upload/download example