Clairvoyant Blog

Clairvoyant is a data and decision engineering company. We design, implement and operate data…

Follow publication

Reactive CRUD APIs with Spring WebFlux

Akash Patel
Clairvoyant Blog
Published in
5 min readJan 11, 2020

--

In this article, we will explore Spring WebFlux —reactive programming for RESTful web services in Spring Framework which is built on Reactive Streams APIs. The objective of this blog is to create a reactive RESTful app that can perform CRUD operations such as Create, Read, Update, and Delete using annotated controllers.

Define Reactive Programming

Reactive programming is a non-blocking application that is asynchronous, even-driven and requires a small number of threads to scale.

An important point of the definition is the back-pressure which is a mechanism to ensure producers don’t overwhelm consumers.

Why do we need an Asynchronous structure?

The simple answer to this question is we want to improve the user experience and make our application more responsive. Also, we want to provide a smooth experience without freezing the main thread.

Spring WebFlux

Spring WebFlux is the new reactive web framework that comes with Spring 5. It is not a replacement for Spring MVC but a fully non-blocking, Reactive Streams back pressure supporting framework that is run on servers like Netty, Jetty, Undertow, etc.

Spring WebFlux majorly uses two publishers:

The Mono

Mono: Returns 0 or 1 element.
The Mono API allows producing only one value.

Mono<String> mono = Mono.just(“Spring Framework”);
Mono<String> mono = Mono.empty();

This is limited to no more than one element.

The Flux

Flux: Returns 0…N elements.

The Flux can be endless, it can produce multiple values. Here, we have a static stream of the thee elements.
Flux<String> flux = Flux.just(“One”, “Two”, “Three”);

Now, let’s create a Spring Boot Project which has all the libraries that are required for the Reactive CRUD APIs.

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

Select the correct technologies, repositories and fill out the details.

After clicking on the generate button, it will download the Spring boot Reactive Application zip file.

Now, we have to extract the zipped file and import this project into STS -Spring Tool Suite.

Open the STS, then click on File → Import… Click on “Import…”

In order to filter the import type, type Existing Maven Projects and select the available result.

Maven → Existing Maven Projects… . Click on next,

Click on the browse button and select the unzipped directory which is a root directory of the Spring Boot Project. Then click on finish.

pom.xml → GitHub

<?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.2.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.reactiveCRUD</groupId><artifactId>universityApp</artifactId><version>0.0.1-SNAPSHOT</version><name>universityApp</name><description>SpringFlux Restful CRUD example</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.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><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></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 → GitHub

logging.level.org.springframework=ERROR
logging.level.com=TRACE
server.port=8080
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=testdb

RegistrationController- It can controll the flow of the application . → GitHub

package com.reactiveCRUD.universityApp.controller;import org.springframework.beans.factory.annotation.Autowired;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.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.reactiveCRUD.universityApp.model.Student;import com.reactiveCRUD.universityApp.service.RegistrationService;import lombok.AllArgsConstructor;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;@RequestMapping("adminDept")@AllArgsConstructor@RestControllerpublic class RegistrationController {@Autowiredprivate RegistrationService registrationService;@GetMappingpublic Flux<Student> getAll() {System.out.println("::GET_ALL Students::");return registrationService.getAll();}@GetMapping("{id}")public Mono<Student> getById(@PathVariable("id") final String id) {System.out.println("::Will Return a Student::");return registrationService.getById(id);}@PutMapping("{id}")public Mono updateById(@PathVariable("id") final String id, @RequestBody final Student student) {System.out.println("::Update the Student record::");return registrationService.update(id, student);}@PostMappingpublic Mono save(@RequestBody final Student student) {System.out.println("Will register the student :: "+ student.getId() + " :: " + student.getFirstName());return registrationService.save(student);}@DeleteMapping("{id}")public Mono delete(@PathVariable final String id) {System.out.println("::Will delete a Student::");return registrationService.delete(id);}}

Model class (i.e. entity class)- It will create a table named “Student Table” in MongoDB. → GitHub

package com.reactiveCRUD.universityApp.model;import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
@Data
@Builder
@AllArgsConstructor
@Document
@ToString
public class Student {

@Id
private String id;
private String firstName;
private String lastName;

public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

RegistrationRepository → GitHub

package com.reactiveCRUD.universityApp.repository;import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import com.reactiveCRUD.universityApp.model.Student;@Repository
public interface RegistrationRepository extends ReactiveMongoRepository<Student, String>{
}

RegistrationService, logic resides under this service class. → GitHub

package com.reactiveCRUD.universityApp.service;import java.util.Objects;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.reactiveCRUD.universityApp.model.Student;
import com.reactiveCRUD.universityApp.repository.RegistrationRepository;
import lombok.AllArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@Transactional
@AllArgsConstructor
public class RegistrationService {
@Autowired
private RegistrationRepository registrationRepository;
public Flux<Student> getAll() {
return registrationRepository.findAll().switchIfEmpty(Flux.empty());
}
public Mono<Student> getById(final String id) {
return registrationRepository.findById(id);
}
public Mono update(final String id, final Student student) {
return registrationRepository.save(student);
}
public Mono save(final Student student) {
return registrationRepository.save(student);
}

public Mono delete(final String id) {
final Mono<Student> dbStudent = getById(id);
if (Objects.isNull(dbStudent)) {
return Mono.empty();
}
return getById(id).switchIfEmpty(Mono.empty()).filter(Objects::nonNull).flatMap(studentToBeDeleted -> registrationRepository
.delete(studentToBeDeleted).then(Mono.just(studentToBeDeleted)));
}
}

Now the application is created. Let’s go to the Spring Boot Application entry point.

Spring boot Application entry point: UniversityAppApplication → GitHub

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

Now, right-click to this file (UniversityAppApplication.java)→Run As. → Spring Boot App.

These are the endpoints for RESTful CRUD operations. Open postman and create the following requests.

POST   method → Insert a student record
GET method → Get all the records, single filtered record
PUT method → Update the existing record
DELETE method → Delete the existing record based on the element

Congratulations! You have successfully completed Reactive Programming 101.

See you all in my next blog. Follow Clairvoyant to get more updates about data engineering.

--

--

Published in Clairvoyant Blog

Clairvoyant is a data and decision engineering company. We design, implement and operate data management platforms with the aim to deliver transformative business value to our customers.

Responses (2)

Write a response