In this tutorial we are going to take a look at creating a Reactive Rest API using Spring Boot, Spring Web flux & R2DBC to get data from H2 in-memory database.
R2DBC – Stands for Reactive Relational Database connectivity.
Spring Web flux – Spring’s reactive-stack web framework.
Reactive stack is non-blocking. As JPA and JDBC are blocking we are using reactive R2DBC to connect to the database non blocking.
Lets jump right into coding.
- First use the preconfigured Spring Initializr url and download the code (or) use the below pom.xml
<?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.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.jsession4d</groupId>
<artifactId>reactivedemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reactivedemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</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>
<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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
- Create the table using schema.sql
Create the schema.sql file under resources and copy the below table creation sql. During the application startup the table customer will be created using this sql.
CREATE TABLE customer (id SERIAL PRIMARY KEY, name VARCHAR(255));
- Create the Customer entity and CustomerRepository classes.
package com.jsession4d.reactivedemo;
import lombok.*;
import org.springframework.data.annotation.Id;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
@Id
String id;
String name;
}
package com.jsession4d.reactivedemo;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
public interface CustomerRepository extends ReactiveCrudRepository<Customer, Long> {
}
Note here we using the ReactiveCrudRepository.
- Now in the Springboot application class add CommandLineRunner bean to insert the Customer objects. Note @Profile(“local”) added to the CommandLineRunner Bean as we need that only for local.
package com.jsession4d.reactivedemo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import java.util.List;
@Slf4j
@SpringBootApplication
public class ReactivedemoApplication {
public static void main(String[] args) {
SpringApplication.run(ReactivedemoApplication.class, args);
}
@Bean
@Profile("local")
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
log.info("Inserting customers");
List<Customer> customerList = Arrays.asList(
Customer.builder().name("Mike").build(),
Customer.builder().name("John").build(),
Customer.builder().name("Scott").build()
);
repository.saveAll(customerList).subscribe();
};
}
}
Next create a CustomerController class to get all the customers from the database.
package com.jsession4d.reactivedemo;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequiredArgsConstructor
public class CustomerController {
final CustomerRepository customerRepository;
@GetMapping("/customers")
public Flux<Customer> getCustomers(){
return customerRepository.findAll();
}
}
The easiest way to write a reactive rest api is to use the return type Flux or Mono. Here we used Flux<Customer>, as there are more than 1 customer in the database.
Now start the ReactivedemoApplication with active profile set to “local”. (-Dspring.profiles.active=local). You can see in the log that Netty started on port 8080 and inserting customer records. Netty is a non-blocking reactive server.
2021-08-30 22:59:12.463 INFO 16464 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 258 ms. Found 1 R2DBC repository interfaces.
2021-08-30 22:59:15.659 INFO 16464 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port 8080
2021-08-30 22:59:15.675 INFO 16464 --- [ main] c.j.r.ReactivedemoApplication : Started ReactivedemoApplication in 5.599 seconds (JVM running for 6.614)
2021-08-30 22:59:15.678 INFO 16464 --- [ main] c.j.r.ReactivedemoApplication : Inserting customers
Next when you hit the url http://localhost:8080/customers in a web browser you can see all the 3 customers from the database.

Junit:
Here we test the restcontroller using the reactive WebTestClient.
Add @WebFluxTest annotation. Using this annotation will disable full auto-configuration and instead apply only configuration relevant to WebFlux tests (i.e. @Controller
, @ControllerAdvice
, @JsonComponent
, Converter
/GenericConverter
, and WebFluxConfigurer
beans but not @Component
, @Service
or @Repository
beans).
The full Junit Code is provided below.
package com.jsession4d.reactivedemo;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
@WebFluxTest
@ActiveProfiles("test")
public class CustomerControllerTest {
@Autowired
private WebTestClient client;
@MockBean
CustomerRepository customerRepository;
@Test
public void getCustomers(){
Mockito.when(this.customerRepository.findAll()).thenReturn(Flux.just(
new Customer("1", "Mike"), new Customer("2", "John")));
this.client.get()
.uri("/customers")
.accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.[0].id").isEqualTo("1")
.jsonPath("$.[0].name").isEqualTo("Mike")
.jsonPath("$[1].id").isEqualTo("2")
.jsonPath("$[1].name").isEqualTo("John");
}
}
Conclusion: Reactive Spring is non blocking and asynchronous. It does more work with fewer resources. But still it is a fairly new concept in Spring and many features are still under development. This tutorial works with Spring Boot version 2.5.4. It might not work with some of the previous versions.
And congratulations on your first reactive spring boot API.