In this tutorial, we’re gonna build a Spring Boot WebFlux Rest API example – CRUD application that uses Spring Data Reactive (R2DBC) to interact with embedded database. You’ll know:
- Overview of Reactive Programming and handling Blocking and Non-blocking requests
- How to configure Spring Data Reactive, R2DBC to work with Database
- How to define Data Models and Repository interfaces
- Way to create Spring Rest Controller to process HTTP requests
- Way to use Spring Data R2DBC to interact with Database
More Practice:
– Spring WebFlux File upload example
– Spring Boot R2DBC + MySQL example
– Spring Boot R2DBC + PostgreSQL example
– Spring Boot Reactive + MongoDB example
– Spring Boot Thymeleaf CRUD example
– Secure Spring Boot App with Spring Security & JWT Authentication
– Spring Boot Rest XML example – Web service with XML Response
– Spring Boot + GraphQL + MySQL example
– Spring Boot Multipart File upload example
– Spring Boot Pagination and Sorting example
Associations:
– Spring Boot One To One example with JPA, Hibernate
– Spring Boot One To Many example with JPA, Hibernate
– Spring Boot Many to Many example with JPA, Hibernate
Fullstack:
– Vue + Spring Boot example
– Angular 8 + Spring Boot example
– Angular 10 + Spring Boot example
– Angular 11 + Spring Boot example
– Angular 12 + Spring Boot example
– Angular 13 + Spring Boot example
– Angular 14 + Spring Boot example
– Angular 15 + Spring Boot example
– React + Spring Boot example
Contents
- Reactive Programming overview
- Handle Blocking and Non-blocking requests
- Spring WebFlux
- Spring Boot WebFlux Rest API example
- Technology
- Project Structure
- Create & Setup Spring Boot project
- Configure the database
- @EnableR2dbcRepositories
- Define Data Model
- Create Repository Interface
- Create Data Service
- Create Spring WebFlux Rest API Controller
- Enable WebFlux
- Create the Database Schema
- Run & Check
- Conclusion
- Source Code
- Further Reading
Reactive Programming overview
Reactive Programming is a programming paradigm that is commonly used in web and mobile development. It provides a non-blocking, asynchronous approach to handling data, which helps manage data flow and synchronization between components. It is supported by several frameworks and libraries (such as Project Reactor, RxJava, Spring WebFlux…).
How it works
The data flow in an application is considered as a stream of events and reacting to changes in the data by triggering a cascade of events. The basic building blocks of Reactive Programming are:
- Observables: data sources that represent a stream of events. They emit items over time, and Subscribers can observe and react to these events.
- Subscribers: components that consume events from Observables. They can perform some actions, such as updating the state of the system, in response to the events they receive.
- Operators: functions that can be applied to Observables to manipulate the events they emit. Operator could be filter, transform,…
Advantages and Disadvantages
Reactive Programming provides a flexible and powerful approach to building modern, responsive, and scalable systems.
- Scalability: enables systems to scale smoothly, even when dealing with large amounts of data or user traffic, by providing a non-blocking, asynchronous approach to handling data.
- Responsiveness: emphasizes the design of systems that respond quickly to changes in the input data, leading to improved user experience and application responsiveness.
- Resilience: provides tools and patterns for building fault-tolerant and self-healing systems, allowing them to gracefully handle and recover from failures.
- Loose coupling: promotes the development of loosely coupled components, which are easier to maintain, test, and evolve over time.
- Modularity: supports the development of modular and composable systems, where complex functionality can be built by combining simple, reusable building blocks.
Reactive Programming may not be the best fit for all projects or scenarios. You need to evaluate carefully the trade-offs before deciding to use it.
Here are some drawbacks:
- Complexity: for developers who are unfamiliar with its concepts and techniques, Reactive Programming can add complexity to the code
- Debugging difficulties: this can be challenging due to the asynchronous and event-driven nature of the code.
- Learning curve: you need to have a different mindset and approach to programming, which can take time and effort.
- Performance: comparing to traditional, synchronous approaches, performance will be impacted when adding abstractions of reactive frameworks and libraries.
- Tool: tooling and development support may still be limited in some contexts.
Otherwise, some of these disadvantages above may be mitigated with experience and proper implementation.
Handle Blocking and Non-blocking requests
Blocking API requests are typically used in simple applications where:
– the processing time for each request is short
– the number of concurrent requests is low
The client sends a request to the API, and the API blocks all other requests until it completes processing the request and sends a response back to the client. Spring provides support for blocking API calls through its traditional servlet-based web framework, Spring MVC.
For high-performance systems, implementing non-blocking APIs is a better choice.
The client sends a request and immediately receives a response, allowing it to continue processing while the API continues to process the request in the background. This enables multiple requests to be processed concurrently without blocking each other, resulting in improved performance and scalability.
To handle non-blocking requests in Spring, you can use the @Async
annotation to annotate methods that should be executed asynchronously, and use the TaskExecutor
interface to control the execution of asynchronous tasks.
// Annotate a method with @Async
@Service
public class AsyncService {
@Async
public Future processRequest() {
// Perform time-consuming task
return new AsyncResult<>("Task completed successfully");
}
}
// Configure a TaskExecutor in Spring configuration class
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("AsyncProcess-");
executor.initialize();
return executor;
}
}
// Call the annotated method from controller
@RestController
public class RequestController {
@Autowired
private AsyncService asyncService;
@GetMapping("/process")
public String processRequest() throws InterruptedException, ExecutionException {
Future result = asyncService.processRequest();
while (!result.isDone());
return result.get();
}
}
Or you can also use the Reactor (Mono, Flux) and Spring WebFlux.
Spring WebFlux
Spring WebFlux (introduced in Spring 5) is a non-blocking, reactive web framework built on top of the Spring framework.
In contrast to the blocking model of the Spring MVC framework, Spring WebFlux is designed to handle large numbers of requests concurrently, with low latency and high throughput. It provides Reactive Programming model, which enables the framework to handle requests as streams of events, rather than as individual requests.
Spring WebFlux supports two programming models:
- Annotation-based: similar to the traditional Spring MVC framework, this model uses annotations to map requests to handler methods.
For example:
@RestController
public class WebFluxController {
@Autowired
private ReactiveRepository repository;
@GetMapping("/mono")
public Mono<String> getMonoResult() {
return Mono.just("Result from Mono");
}
@GetMapping("/flux")
public Flux<String> getFluxResult() {
return Flux.fromIterable(repository.findAll())
.map(item -> item + " processed");
}
}
For example:
@Configuration
public class RouterConfig {
@Bean
public RouterFunction<ServerResponse> routes(GreetingHandler greetingHandler) {
return RouterFunctions
.route(RequestPredicates.GET("/mono"), greetingHandler::greeting);
}
}
@Component
public class GreetingHandler {
public Mono greeting(ServerRequest request) {
return ServerResponse.ok().body(Mono.just("Result from Mono"), String.class);
}
}
Spring WebFlux provides several features for building reactive web applications, including a router function for handling requests, support for server-sent events and WebSockets, and an extensive set of reactive libraries for handling data and processing events.
Spring Boot WebFlux Rest API example
We will build a Spring Boot CRUD Rest Api using Spring WebFlux for a Tutorial application in that:
- Each Tutorial has id, title, description, published status.
- Apis help to create, retrieve, update, delete Tutorials.
- Apis also support custom finder methods such as find by published status or by title.
These are APIs that we need to provide:
Methods | Urls | Actions |
---|---|---|
POST | /api/tutorials | create new Tutorial |
GET | /api/tutorials | retrieve all Tutorials |
GET | /api/tutorials/:id | retrieve a Tutorial by :id |
PUT | /api/tutorials/:id | update a Tutorial by :id |
DELETE | /api/tutorials/:id | delete a Tutorial by :id |
DELETE | /api/tutorials | delete all Tutorials |
GET | /api/tutorials/published | find all published Tutorials |
GET | /api/tutorials?title=[keyword] | find all Tutorials which title contains keyword |
Technology
- Java 11/17
- Spring Boot 3 (with Spring WebFlux, Spring Data R2DBC)
- H2 (embedded database)
- Maven 3.6.1
Project Structure
Let me explain it briefly.
– Tutorial
data model class.
– TutorialRepository
is an interface that extends R2dbcRepository to interact with the database. It is autowired in TutorialService
.
– TutorialService
is a service component that uses TutorialRepository
and provides CRUD methods and custom finder methods for TutorialController
.
– TutorialController
is a RestController which has request mapping methods for RESTful requests such as: getAllTutorials, createTutorial, updateTutorial, deleteTutorial, findByPublished…
– Configuration for Spring Data R2DBC is in application.properties.
– schema.sql has SQL statement for initializing database table.
– pom.xml contains dependencies for Spring Boot, WebFlux, R2DBC and H2 database.
Create & Setup Spring Boot project
Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.
Then open pom.xml and add these dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Configure the database
Under src/main/resources folder, open application.properties and write these lines.
#spring.data.r2dbc.repositories.enabled=true
spring.r2dbc.url=r2dbc:h2:file:///./testdb
The connection URL is set to r2dbc:h2:file:///./[database-name]
, which indicates that you are using a disk-based database.
For in-memory database, use r2dbc:h2:mem:///[database-name]
instead.
spring.data.r2dbc.repositories.enabled
determines the activation of R2DBC repositories in a Spring Boot application.
By default, R2DBC repository support is enabled in a Spring Boot application. If you want to disable R2DBC repository support, you can set the spring.data.r2dbc.repositories.enabled
property to false
.
@EnableR2dbcRepositories
@EnableR2dbcRepositories
is a Spring annotation that is used to enable R2DBC repositories in a Spring Boot application. It provides a convenient way to create a repository layer in a Spring Boot application that uses R2DBC to interact with a database.
Because R2DBC repository support is enabled in our Spring Boot application by default (spring.data.r2dbc.repositories.enabled=true
), so that the @EnableR2dbcRepositories
is not necessary.
The @EnableR2dbcRepositories
annotation could be added to a configuration class in your application, typically the main class that is annotated with @SpringBootApplication
:
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
@EnableR2dbcRepositories
@SpringBootApplication
public class SpringBootWebfluxExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebfluxExampleApplication.class, args);
}
}
Define Data Model
Our Data model is Tutorial with four fields: id, title, description, published.
In model package, we define Tutorial
class.
model/Tutorial.java
package com.bezkoder.spring.webflux.model;
import org.springframework.data.annotation.Id;
public class Tutorial {
@Id
private int id;
private String title;
private String description;
private boolean published;
public Tutorial() {
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
// getters and setters
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
Create Repository Interface
Let’s create a repository to interact with Tutorials from the database.
In repository package, create TutorialRepository
interface that extends R2dbcRepository
(which extends Spring Data Reactive ReactiveCrudRepository
).
repository/TutorialRepository.java
package com.bezkoder.spring.webflux.repository;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.stereotype.Repository;
import com.bezkoder.spring.webflux.model.Tutorial;
import reactor.core.publisher.Flux;
@Repository
public interface TutorialRepository extends R2dbcRepository<Tutorial, Integer>{
Flux<Tutorial> findByTitleContaining(String title);
Flux<Tutorial> findByPublished(boolean isPublished);
}
Now we can use ReactiveCrudRepository
‘s methods: save()
, findById()
, findAll()
, count()
, delete()
, deleteById()
, deleteAll()
… without implementing these methods.
We also define custom finder methods:
– findByPublished()
: returns all Tutorials with published
having value as input published
.
– findByTitleContaining()
: returns all Tutorials which title contains input title
.
The implementation is plugged in by Spring Data R2DBC automatically.
Create Data Service
Let’s create a service that uses TutorialRepository
to implement CRUD Operations and custom finder methods.
In service package, create TutorialService
.
service/TutorialService.java
package com.bezkoder.spring.webflux.service;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bezkoder.spring.webflux.model.Tutorial;
import com.bezkoder.spring.webflux.repository.TutorialRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class TutorialService {
@Autowired
TutorialRepository tutorialRepository;
public Flux<Tutorial> findAll() {
return tutorialRepository.findAll();
}
public Flux<Tutorial> findByTitleContaining(String title) {
return tutorialRepository.findByTitleContaining(title);
}
public Mono<Tutorial> findById(int id) {
return tutorialRepository.findById(id);
}
public Mono<Tutorial> save(Tutorial tutorial) {
return tutorialRepository.save(tutorial);
}
public Mono<Tutorial> update(int id, Tutorial tutorial) {
return tutorialRepository.findById(id).map(Optional::of).defaultIfEmpty(Optional.empty())
.flatMap(optionalTutorial -> {
if (optionalTutorial.isPresent()) {
tutorial.setId(id);
return tutorialRepository.save(tutorial);
}
return Mono.empty();
});
}
public Mono<Void> deleteById(int id) {
return tutorialRepository.deleteById(id);
}
public Mono<Void> deleteAll() {
return tutorialRepository.deleteAll();
}
public Flux<Tutorial> findByPublished(boolean isPublished) {
return tutorialRepository.findByPublished(isPublished);
}
}
– TutorialService
class is annotated with @Service
to indicate that it is a service component. Spring will create a singleton bean for this class and manage its lifecycle.
– We use @Autowired
to inject TutorialRepository
bean to local variable.
Create Spring WebFlux Rest API Controller
Finally, we create a controller that provides APIs for creating, retrieving, updating, deleting and finding Tutorials.
controller/TutorialController.java
package com.bezkoder.spring.webflux.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.CrossOrigin;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.bezkoder.spring.webflux.model.Tutorial;
import com.bezkoder.spring.webflux.service.TutorialService;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {
@Autowired
TutorialService tutorialService;
@GetMapping("/tutorials")
@ResponseStatus(HttpStatus.OK)
public Flux<Tutorial> getAllTutorials(@RequestParam(required = false) String title) {
if (title == null)
return tutorialService.findAll();
else
return tutorialService.findByTitleContaining(title);
}
@GetMapping("/tutorials/{id}")
@ResponseStatus(HttpStatus.OK)
public Mono<Tutorial> getTutorialById(@PathVariable("id") int id) {
return tutorialService.findById(id);
}
@PostMapping("/tutorials")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
return tutorialService.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
}
@PutMapping("/tutorials/{id}")
@ResponseStatus(HttpStatus.OK)
public Mono<Tutorial> updateTutorial(@PathVariable("id") int id, @RequestBody Tutorial tutorial) {
return tutorialService.update(id, tutorial);
}
@DeleteMapping("/tutorials/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public Mono<Void> deleteTutorial(@PathVariable("id") int id) {
return tutorialService.deleteById(id);
}
@DeleteMapping("/tutorials")
@ResponseStatus(HttpStatus.NO_CONTENT)
public Mono<Void> deleteAllTutorials() {
return tutorialService.deleteAll();
}
@GetMapping("/tutorials/published")
@ResponseStatus(HttpStatus.OK)
public Flux<Tutorial> findByPublished() {
return tutorialService.findByPublished(true);
}
}
– @CrossOrigin
is for configuring allowed origins.
– @RestController
annotation is used to define a controller and to indicate that the return value of the methods should be be bound to the web response body.
– @RequestMapping("/api")
declares that all Apis’ url in the controller will start with /api
.
– We use @Autowired
to inject TutorialService
bean to local variable.
Enable WebFlux
We use @EnableWebFlux to enable support for reactive web application using the Spring WebFlux framework.
SpringBootWebfluxExampleApplication.java
package com.bezkoder.spring.webflux;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@EnableWebFlux
@SpringBootApplication
public class SpringBootWebfluxExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebfluxExampleApplication.class, args);
}
}
Create the Database Schema
In src/main/resources folder, create schema.sql file with following content:
CREATE TABLE IF NOT EXISTS tutorial (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255), description VARCHAR(255), published BOOLEAN, PRIMARY KEY (id));
Spring Data R2DBC ConnectionFactoryInitializer
provides a convenient way to configure and initialize a connection factory for a reactive database connection in a Spring application. It will scan schema.sql
in the classpath, execute SQL script to initialize the database when the database is connected.
SpringBootWebfluxExampleApplication.java
package com.bezkoder.spring.webflux;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
import org.springframework.web.reactive.config.EnableWebFlux;
import io.r2dbc.spi.ConnectionFactory;
@EnableWebFlux
@SpringBootApplication
public class SpringBootWebfluxExampleApplication {
@Bean
ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
return initializer;
}
public static void main(String[] args) {
SpringApplication.run(SpringBootWebfluxExampleApplication.class, args);
}
}
Run & Check
Run Spring Boot application with command: mvn spring-boot:run
.
Create some Tutorials:
Retrieve all Tutorials:
Update some Tutorials:
Retrieve a Tutorial by Id:
Find all published Tutorials:
Find all Tutorials which title contains a certain string:
Delete a Tutorial:
Check all current Tutorials:
Delete all Tutorials and check:
You can also check this Spring Boot App with Client in one of these posts:
- Simple HTTP Client using Axios
- Simple HTTP Client using Fetch API
- Angular 8 CRUD Application example with Web API
- Angular 10 CRUD example with Web API
- Angular 11 CRUD example with Web API
- Angular 12 CRUD example with Web API
- Angular 13 CRUD example with Web API
- Angular 14 CRUD example with Web API
- Angular 15 CRUD example with Web API
- Vue 2 CRUD example with Axios
- Vue 3 CRUD example with Axios
- React CRUD example with Axios
- React Redux CRUD example
Conclusion
Today we’ve known what is Reactive Programming, overview of Blocking and Non-blocking API call, then we’ve built a Spring Boot WebFlux Rest API example successfully using WebFlux Framework, Spring Data Reactive, R2DBC for CRUD and custom finder methods.
Happy learning! See you again.
Source Code
You can find the complete source code for this tutorial on Github.
Further Reading
Fullstack CRUD App:
– Spring Boot Thymeleaf example
– Vue + Spring Boot example
– Angular 8 + Spring Boot example
– Angular 10 + Spring Boot example
– Angular 11 + Spring Boot example
– Angular 12 + Spring Boot example
– Angular 13 + Spring Boot example
– Angular 14 + Spring Boot example
– Angular 15 + Spring Boot example
– React + Spring Boot example
More Practice:
– Spring WebFlux File upload example
– Spring Boot R2DBC + MySQL example
– Spring Boot R2DBC + PostgreSQL example
– Spring Boot Reactive + MongoDB example
– Secure Spring Boot with Spring Security & JWT Authentication
– Spring Boot Rest XML example – Web service with XML Response
– Spring Boot Multipart File upload example
– Spring Boot Pagination and Sorting example