In this post, we’ll implement a sample REST API that uses Spring Webflux with Spring Data Cassandra.
· Prerequisites
· Overview
∘ What is Apache Cassandra?
∘ Cassandra keyspaces
∘ Cassandra Setup with Docker
· Project Setup
∘ Configure Spring Data Cassandra
∘ Property Mapping Table:
· CRUD API
∘ Create Book Repository interface
∘ Service Layer
∘ Create Book Controller
· Test the application
· Conclusion
· References
Prerequisites
This is the list of all the prerequisites:
- Spring Boot 3+
- Maven 3.6.3 or later
- Java 21
- IntelliJ IDEA, Visual Studio Code, or another IDE
- Docker installed
- Docker compose is installed
- 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:
-- ===========================
-- KEYSPACE
-- ===========================
CREATE KEYSPACE IF NOT EXISTS keyspaceboottech
WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }
AND DURABLE_WRITES = true;
USE keyspaceboottech;
-- ===========================
-- TABLE: author
-- ===========================
CREATE TABLE IF NOT EXISTS author (
id uuid PRIMARY KEY,
lastname text,
firstname text
);
CREATE INDEX IF NOT EXISTS author_lastname_idx
ON author (lastname);
-- ===========================
-- TABLE: book
-- ===========================
CREATE TABLE IF NOT EXISTS book (
id uuid PRIMARY KEY,
title text,
isbn text,
description text,
language text,
publication_date date,
pages int,
price double
);
CREATE INDEX IF NOT EXISTS book_isbn_idx
ON book (isbn);
CREATE INDEX IF NOT EXISTS book_title_idx
ON book (title);
Cassandra Setup with Docker
To start our Cassandra integration with a Spring reactive application, we need to start a Cassandra database.
For this, we’ll 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
container_name: cassandra
environment:
MAX_HEAP_SIZE: "256M"
HEAP_NEWSIZE: "128M"
CASSANDRA_CLUSTER_NAME: "MySrpringReactiveCluster"
CASSANDRA_SEEDS: "cassandra"
volumes:
- cassandra_data:/var/lib/cassandra
ports:
- "7000:7000" # intra-node communication
- "7001:7001" # SSL intra-node
- "7199:7199" # JMX
- "9042:9042" # CQL
healthcheck:
test: ["CMD-SHELL", "cqlsh -e 'DESCRIBE KEYSPACES'"]
interval: 20s
timeout: 10s
retries: 10
cassandra-load-keyspace:
container_name: cassandra-load-keyspace
image: cassandra:latest
depends_on:
cassandra:
condition: service_healthy
volumes:
- ./ddl-schema.cql:/schema.cql
entrypoint: ["/bin/bash", "-c", "echo 'Loading schema...' && cqlsh cassandra -f /schema.cql"]
volumes:
cassandra_data:
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.
Project Setup
We’ll 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.
Here is the pom.xml dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-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-starter-validation</artifactId>
</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>
Configure Spring Data Cassandra
One of the first tasks when using Apache Cassandra with Spring is to create a com.datastax.oss.driver.api.core.CqlSession object by using the Spring IoC container. You can do so either by using Java-based bean metadata or by using XML or properties files.
spring:
cassandra:
contact-points: localhost
port: 9042
keyspace-name: keyspaceboottech
local-datacenter: datacenter1
schema-action: none
contact-points and keyspace-name are the required fields.
Property Mapping Table:

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;
@Column("pages")
private int page;
private String isbn;
private String description;
private String language;
@Column("publication_date")
private LocalDate publication;
private double price;
}
– @Table identifies this model to be persisted to Cassandra as a book table.
– @PrimaryKey specifies the primary key field of this entity.
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table("author")
public class Author {
@PrimaryKey
private UUID id;
private String lastname;
private String firstname;
}
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/books")
@Slf4j
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public Flux<Book> getAllBooks() {
return bookService.getAll();
}
@GetMapping("/{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
@ResponseStatus(HttpStatus.CREATED)
public Mono<Book> addBook(@RequestBody @Valid Book book) {
return bookService.add(book);
}
@PutMapping("/{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("/{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
- Get All Books
- Update book
In the cqlsh console, we can check the Cassandra database:
select * from keyspaceboottech.book;

Conclusion
🏁 Well done !!. We’ve built a REST CRUD API with Spring WebFlux and Spring Data Cassandra.
Building reactive REST APIs with Spring WebFlux and Spring Data Cassandra provides a powerful, scalable foundation for modern applications. The non-blocking nature of this stack allows you to handle massive concurrency with limited resources, while reactive streams provide a robust way to handle data flow with back pressure.
The combination of Spring WebFlux’s reactive web capabilities with Cassandra’s high-performance, scalable database creates an excellent foundation for microservices that require handling high throughput and low latency.
The complete source code is available on GitHub.
Support me through GitHub Sponsors.
Thank you for reading!! See you in the next post.
Last updated: Dec 8, 2025


