In this post, we’ll implement a sample REST API that uses Spring Data R2DBC with PostgreSQL Database on Amazon Relational Database Service.
Overview
The Reactive Relational Database Connectivity (R2DBC) project brings reactive programming APIs to relational databases. It defines a non-blocking SPI for database driver implementors and client library authors. R2DBC drivers fully implement the database wire protocol on top of a non-blocking I/O layer.
Creating an RDS PostgreSQL Instance
Amazon Relational Database Service (Amazon RDS) makes it easy to set up, operate, and scale a relational database in the cloud. It provides cost-efficient and resizable capacity while automating time-consuming administration tasks, such as hardware provisioning, database setup, patching, and backups. It frees you to focus on your applications so you can give them the fast performance, high availability, security, and compatibility they need. — https://aws.amazon.com/rds/
- Log in to the AWS Management Console and open the Amazon RDS service.
- Create a PostgreSQL DB instance on the right button.




3. Configure Security Group for PostgreSQL DB instance
We want to connect to our instance outside the AWS environment. By default, the instance’s VPC security group does not allow any inbound connections. We need to add an inbound rule on the instance security group VPC

4. Connecting to the PostgreSQL DB instance
Open pgAdmin4 then connect to our instance.

Now, we’ll create the first database “book-rdbc-db”
Spring Boot App Setup
We will start by creating a simple Spring Boot project from start.spring.io, with the following dependencies: Spring Reactive Web, Spring Data R2DBC, Spring Data JPA, Liquibase Migration, PostgreSQL Driver, Spring Data JDBC, R2DBC driver, and Lombok.
Spring Data R2DBC supports drivers through R2DBC’s pluggable SPI mechanism. It reacts to database specifics by inspecting the ConnectionFactory and selecting the appropriate database dialect.
Database initialization
The Spring Data reactive driver requires a configuration to connect to the PostgresDB. We can either add our database connection details in application.yml.
rds:
db:
url: r2dbc-instance.ceox6edjpuog.us-east-2.rds.amazonaws.com/book-rdbc-db
username: postgres
password: postgres
spring:
liquibase:
url: jdbc:postgresql://${rds.db.url}
user: ${rds.db.username}
password: ${rds.db.password}
change-log: classpath:changelog/changelog-master.xml
r2dbc:
url: r2dbc:postgresql://${rds.db.url}
username: ${rds.db.username}
password: ${rds.db.password}
We added liquibase configurations to create the database schema and insert data before use by Spring Data R2DBC.
We will be creating a new changelog that creates a new column. Here is our db.changelog-1.0.xml.
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet id="202010211812" author="aek">
<createTable tableName="book">
<column name="id" type="bigint">
<constraints primaryKey="true" primaryKeyName="book_id_pk" />
</column>
<column name="title" type="varchar(100)">
<constraints unique="true" uniqueConstraintName="book_title_unq" />
</column>
<column name="isbn" type="varchar(255)"/>
<column name="description" type="varchar(255)"/>
<column name="page" type="int"/>
<column name="price" type="double"/>
</createTable>
<createTable tableName="author">
<column name="id" type="bigint">
<constraints primaryKey="true" primaryKeyName="author_id_pk" />
</column>
<column name="lastname" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="firstName" type="varchar(255)"/>
</createTable>
<addAutoIncrement tableName="book" columnName="id" columnDataType="bigint" startWith="1" incrementBy="1" />
<addAutoIncrement tableName="author" columnName="id" columnDataType="bigint" startWith="1" incrementBy="1" />
</changeSet>
<changeSet id="202010211812-1-data" author="aek" context="faker">
<loadData
file="changelog/data/book.csv"
separator=","
tableName="book"
usePreparedStatements="true">
<column name="title" type="string"/>
<column name="page" type="numeric"/>
<column name="isbn" type="string"/>
<column name="description" type="string"/>
<column name="price" type="numeric"/>
</loadData>
</changeSet>
</databaseChangeLog>
Let’s get to the code
Next, add the Java code that will use R2DBC to store and retrieve data from your PostgreSQL database.
Our entity class to represent the Book:

- The id of an entity should be annotated with Spring Data’s @Id annotation.
- Do note that we can not add @Entity (It’s not JPA)
- The @Table annotation is optional.
- Lombok is recommended to be used to avoid boilerplate code.
To manage that class, you’ll need a repository.

To configure R2DBC repositories, we use the @EnableR2dbcRepositories annotation in the Spring boot launcher class.
Complete the implementation by creating the controller.
@RestController
@RequestMapping("/api/book")
public class BookController {
private final BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<Book> createBook(@RequestBody Book book) {
return bookRepository.save(book);
}
@GetMapping
public Flux<Book> getBooks() {
return bookRepository.findAll();
}
@GetMapping("/{bookId}")
public Mono<ResponseEntity<Book>> getBookById(@PathVariable long bookId){
return bookRepository.findById(bookId)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PutMapping("{bookId}")
public Mono<ResponseEntity<Book>> updateBook(@PathVariable long bookId, @RequestBody Mono<Book> bookMono){
return bookRepository.findById(bookId)
.flatMap(book -> bookMono.map(u -> {
book.setDescription(u.getDescription());
book.setPrice(u.getPrice());
book.setIsbn(u.getIsbn());
book.setPrice(u.getPrice());
book.setPage(u.getPage());
return book;
}))
.flatMap(bookRepository::save)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteBook(@PathVariable long id) {
return bookRepository.findById(id)
.flatMap(s ->
bookRepository.delete(s)
.then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
)
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
Test the REST API
- GET all Book

- Create new Book

Congratulations 😊!! Our REST application with Spring Data R2DBC is ready.
Conclusion
In this story, we implemented a REST API application that uses Spring Data R2DBC with PostgreSQL Database on Amazon Relational Database Service.
Although Spring Data R2DBC has advantages. It is not a full ORM like JPA and does not currently support relationships. So developers have to put more manual effort to perform join actions.
The complete source code is available on GitHub.