In this post, we’ll implement a sample REST API that uses Spring WebFlux with Spring Data Redis Reactive.
Redis is an open-source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.
Prerequisites
- Spring Boot 2.6.5
- Maven 3.6.+
- Java 11 or later
- Redis 3.2 or later
Getting Started
We will start by creating a simple Spring Boot project from start.spring.io, with the following dependencies: Spring Reactive Web, Spring Data Reactive Redis, Lombok, and Validation.

Project Structure
Here is our project structure:

Configuration
One of the first tasks when using Redis and Spring is to establish a connection with our Redis server.
We need to create our first reactiveRedisConnectionFactory bean with the hostname and port of the Redis server.

The LettuceConnectionFactory class is the factory that creates Lettuce-based connections. It create a new LettuceConnection on each call to getConnection().
The second step in the configuration is to add a second redisOperations bean method, which takes a ReactiveRedisConnectionFactory and returns a ReactiveRedisTemplate. This bean uses the Jackson library by configuring a Jackson2JsonRedisSerializer to perform automatic serialization/deserialization between the given objects and the underlying binary data in the Redis store. We can inject this bean wherever we need to access Redis
@Configuration
public class ReactiveRedisConfiguration {
private final Environment env;
public ReactiveRedisConfiguration(Environment env) {
this.env = env;
}
@Bean
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
return new LettuceConnectionFactory(Objects.requireNonNull(env.getProperty("spring.redis.host")),
Integer.parseInt(Objects.requireNonNull(env.getProperty("spring.redis.port"))));
}
@Bean
public ReactiveRedisOperations<String, Object> redisOperations(ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
RedisSerializationContext.RedisSerializationContextBuilder<String, Object> builder =
RedisSerializationContext.newSerializationContext(new StringRedisSerializer());
RedisSerializationContext<String, Object> context = builder.value(serializer).hashValue(serializer)
.hashKey(serializer).build();
return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, context);
}
}
CRUD API
To get started, we need a model class. For this story, we have a Book model class.
@RedisHash
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
@NotNull
private String id;
private String title;
private int page;
private String isbn;
private String description;
private double price;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate publicationDate;
private String language;
}
@RedisHashannotation marks Objects as aggregate roots to be stored in a Redis hash.@Idto provide primary keys to mapped objects.
We now have the generic component ReactiveRedisComponent which contains all the common crud methods. This class will be used with different repository classes.
@SuppressWarnings("rawtypes")
@Slf4j
@Component
public class ReactiveRedisComponent {
private final ReactiveRedisOperations<String, Object> redisOperations;
public ReactiveRedisComponent(ReactiveRedisOperations<String, Object> redisOperations) {
this.redisOperations = redisOperations;
}
/**
* Set key and value into a hash key
* @param key key value - must not be null.
* @param hashKey hash key value - must not be null.
* @param val Object value
* @return Mono of object
*/
public Mono<Object> set(String key, String hashKey, Object val) {
return redisOperations.opsForHash().put(key, hashKey, val).map(b -> val);
}
/**
* @param key key value - must not be null.
* @return Flux of Object
*/
public Flux<Object> get(@NotNull String key){
return redisOperations.opsForHash().values(key);
}
/**
* Get value for given hashKey from hash at key.
* @param key key value - must not be null.
* @param hashKey hash key value - must not be null.
* @return Object
*/
public Mono<Object> get(String key, Object hashKey) {
return redisOperations.opsForHash().get(key, hashKey);
}
/**
* Delete a key that contained in a hash key.
* @param key key value - must not be null.
* @param hashKey hash key value - must not be null.
* @return 1 Success or 0 Error
*/
public Mono<Long> remove(String key, Object hashKey) {
return redisOperations.opsForHash().remove(key, hashKey);
}
}
RedisBookRepository.java
@Repository
@RequiredArgsConstructor
public class RedisBookRepository implements BookRepository {
private final ReactiveRedisComponent reactiveRedisComponent;
@Override
public Mono<Book> save(Book book) {
return reactiveRedisComponent.set(BOOK_KEY, book.getId(), book).map(b -> book);
}
@Override
public Mono<Book> get(String key) {
return reactiveRedisComponent.get(BOOK_KEY, key).flatMap(d -> Mono.just(ObjectMapperUtils.objectMapper(d, Book.class)));
}
@Override
public Flux<Book> getAll(){
return reactiveRedisComponent.get(BOOK_KEY).map(b -> ObjectMapperUtils.objectMapper(b, Book.class))
.collectList().flatMapMany(Flux::fromIterable);
}
@Override
public Mono<Long> delete(String id) {
return reactiveRedisComponent.remove(BOOK_KEY,id);
}
}
BookServiceImpl.java
@RequiredArgsConstructor
@Service
public class BookServiceImpl implements BookService {
private final RedisBookRepository bookRepository;
@Override
public Mono<Book> create(Book book) {
return bookRepository.save(book);
}
@Override
public Flux<Book> getAll(){
return bookRepository.getAll();
}
@Override
public Mono<Book> getOne(String id){
return bookRepository.get(id);
}
@Override
public Mono<Long> deleteById(String id) {
return bookRepository.delete(id);
}
}
BookController.java
@RestController
@RequestMapping("/v1")
@RequiredArgsConstructor
public class BookController {
private final BookServiceImpl bookService;
@PostMapping("/book")
@ResponseStatus(HttpStatus.CREATED)
public Mono<Book> addBook(@RequestBody @Valid Book book) {
return bookService.create(book);
}
@GetMapping("/book")
public Flux<Book> getAllBooks() {
return bookService.getAll();
}
@GetMapping("/book/{id}")
public Mono<Book> getBook(@PathVariable String id) {
return bookService.getOne(id);
}
@DeleteMapping("/book/{id}")
public Mono<Long> deleteBook(@PathVariable String id) {
return bookService.deleteById(id);
}
}
Test the application
Now we can run our application and test it.

- Create Book
Get All Book
Get By book Id

Summary
Congratulations! We have just developed a full reactive application with Spring WebFlux and Spring Data Redis Reactive.
The complete source code is available on GitHub.


