In this post, we’ll implement a sample REST API that uses Spring Webflux with Spring Data Cassandra.
Prerequisites
This is the list of all the prerequisites:
- Spring Boot 2.7.0
- Gradle
- Java 17
- Docker installed
- Docker compose is installed
- Cassandra
- Postman
Overview
What is Apache Cassandra?

Apache Cassandra is an open source distributed database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. Cassandra offers robust support for clusters spanning multiple datacenters, with asynchronous masterless replication allowing low latency operations for all clients.
Cassandra keyspaces
A keyspace is the top-level database object that controls the replication for the object it contains at each datacenter in the cluster. Keyspaces contain tables, materialized views and user-defined types, functions and aggregates. Typically, a cluster has one keyspace per application. Since replication is controlled on a per-keyspace basis, store data with different replication requirements (at the same datacenter) in different keyspaces. Keyspaces are not a significant map layer within the data model.
Our cassandra ddl schema:
-- Create a keyspace
CREATE KEYSPACE IF NOT EXISTS keyspaceboottech
WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }
AND DURABLE_WRITES = true;
USE keyspaceboottech;
-- Create a tables
CREATE TABLE IF NOT EXISTS author(
id uuid PRIMARY KEY,
lastname text,
firstname text
);
CREATE TABLE IF NOT EXISTS book(
id uuid PRIMARY KEY,
title text,
isbn text,
description text,
language text,
publication date,
page int,
price double
);
Cassandra Setup with Docker
To start our Cassandra integration with Spring reactive application, we need to start a Cassandra database.
For this, we will use Docker containers. We will create a docker-compose file containing all the instructions to run the Cassandra DB instance in standalone mode.
version: '3.8'
services:
cassandra:
image: cassandra:latest
environment:
- "MAX_HEAP_SIZE=256M"
- "HEAP_NEWSIZE=128M"
volumes:
- ./apps/cassandra:/var/lib/cassandra
ports:
- "7000:7000"
- "7001:7001"
- "7199:7199"
- "9042:9042"
- "9160:9160"
cassandra-load-keyspace:
container_name: cassandra-load-keyspace
image: cassandra:latest
depends_on:
- cassandra
volumes:
- ./ddl-schema.cql:/schema.cql
command: /bin/bash -c "sleep 60 && echo loading cassandra keyspace && cqlsh cassandra -f /schema.cql"
Once you’ve created this yml, open your CLI and run the following command:
docker-compose up -d
This command creates a Cassandra database instance, creates a keyspace, and initializes the database schema from ddl-schema.cql file.
Getting Started
We will start by creating a simple Spring reactive project from start.spring.io, with the following dependencies: Spring Reactive Web, Spring Data Reactive for Apache Cassandra, Lombok, and Validation.
Below is the build.gradle project:
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.12.RELEASE'
id 'java'
}
group = 'com.boottech'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-cassandra-reactive'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Project Structure
Our project structure will look like this:

Configure Spring Data Cassandra
The main step is to add cassandra’s connection settings in the application.yml file:
spring:
data:
cassandra:
contact-points: datacenter1
port: 9042
keyspace-name: keyspaceboottech
username: root
password: qwerty
contact-points and keyspace-name are the required fields.
You can also set below configuration using Java configuration. If you want to use Java configuration, use the @EnableCassandraRepositories annotation. The annotation carries the same attributes as the namespace element. If no base package is configured, the infrastructure scans the package of the annotated configuration class.
@Configuration
@EnableCassandraRepositories
class ApplicationConfig extends AbstractReactiveCassandraConfiguration {
@Override
protected String getKeyspaceName() {
return "keyspaceboottech";
}
public String[] getEntityBasePackages() {
return new String[] { "com.boottech.springwebfluxcassandra.domain" };
}
}
CRUD API
To start, we need to create a Bookclass to present a table in Cassandra.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table("book")
public class Book {
@PrimaryKey
private UUID id;
private String title;
private int page;
private String isbn;
private String description;
private String language;
private LocalDate publication;
private double price;
}
– @Table identifies this model to be persisted to Cassandra as book table.
– @PrimaryKey specifies the primary key field of this entity.
Create Book Repository interface
@Repository
public interface BookRepository extends ReactiveCassandraRepository<Book, UUID> {
}
We will use the basic ReactiveCassandraRepository interface which extends ReactiveCrudRepository to offer some variations of the insert method.
Service Layer
The service layer implementation class (BookServiceImpl) will be injected by the BookRepository.
@Slf4j
@Service
public class BookServiceImpl implements BookService {
private final BookRepository repository;
public BookServiceImpl(BookRepository repository) {
this.repository = repository;
}
@Override
public Flux<Book> getAll() {
return repository.findAll();
}
@Override
public Mono<Book> add(Book book) {
book.setId(UUID.randomUUID());
log.info("addBook : {} " , book );
return repository.save(book)
.log();
}
@Override
public Mono<Book> getById(String id) {
return repository.findById(UUID.fromString(id))
.switchIfEmpty(Mono.error(new DataNotFoundException("Book id not found")));
}
@Override
public Mono<Book> update(Book book, String id) {
return repository.findById(UUID.fromString(id))
.flatMap(book1 -> {
book1.setTitle(book.getTitle());
book1.setIsbn(book.getIsbn());
book1.setDescription(book.getDescription());
book1.setLanguage(book.getLanguage());
book1.setPage(book.getPage());
book1.setPrice(book.getPrice());
book1.setPublication(book.getPublication());
return repository.save(book1);
});
}
@Override
public Mono<Void> deleteById(String id) {
return repository.deleteById(UUID.fromString(id));
}
}
Create Book Controller
@RestController
@RequestMapping("/api")
@Slf4j
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("/book")
public Flux<Book> getAllBooks() {
return bookService.getAll();
}
@GetMapping("/book/{id}")
public Mono<ResponseEntity<Book>> getBookById(@PathVariable("id") String id) {
return bookService.getById(id)
.map(book1 -> ResponseEntity.ok()
.body(book1))
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()))
.log();
}
@PostMapping("/book")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Book> addBook(@RequestBody @Valid Book book) {
return bookService.add(book);
}
@PutMapping("/book/{id}")
@ResponseStatus(HttpStatus.OK)
public Mono<ResponseEntity<Book>> updateBook(@RequestBody Book book, @PathVariable String id) {
var updatedBookMono = bookService.update(book, id);
return updatedBookMono
.map(book1 -> ResponseEntity.ok()
.body(book1))
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
@DeleteMapping("/book/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public Mono<Void> deleteBookById(@PathVariable String id){
return bookService.deleteById(id);
}
}
Test the application
Now we can run our application and test it.
- Create Book
In cqlsh console we can check cassandra database:
select * from keyspaceboottech.book;

- Get All Book
- Update book
Conclusion
We’ve built a Rest CRUD API with Spring WebFlux and Spring Data Cassandra.
The complete source code is available on GitHub.


