Building a Spring WebFlux Reactive REST API project using Spring Reactor*. The app will be a **Reactive Book Management System* where you can:
- Create a book π
- Get all books
- Get a book by ID
- Delete a book
- Use MongoDB Reactive Repositories
β Tech Stack
- Spring Boot
- Spring WebFlux
- Reactive MongoDB
- Project Reactor (
Mono,Flux)
π Project Structure
reactive-book-app/
βββ src/
β βββ main/
β βββ java/com/example/book/
β β βββ controller/
β β βββ model/
β β βββ repository/
β β βββ service/
β β βββ BookAppApplication.java
β βββ resources/
β βββ application.yml
βββ pom.xml
π§© 1. pom.xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>reactive-book-app</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
𧬠2. Book Model
package com.example.book.model;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document
@NoArgsConstructor
@AllArgsConstructor
public class Book {
@Id
private String id;
private String title;
private String author;
private Double price;
}
πΎ 3. BookRepository
package com.example.book.repository;
import com.example.book.model.Book;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends ReactiveMongoRepository<Book, String> {
}
π§ 4. BookService
package com.example.book.service;
import com.example.book.model.Book;
import com.example.book.repository.BookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@RequiredArgsConstructor
public class BookService {
private final BookRepository bookRepository;
public Flux<Book> getAllBooks() {
return bookRepository.findAll();
}
public Mono<Book> getBookById(String id) {
return bookRepository.findById(id);
}
public Mono<Book> saveBook(Book book) {
return bookRepository.save(book);
}
public Mono<Void> deleteBook(String id) {
return bookRepository.deleteById(id);
}
}
π 5. BookController
package com.example.book.controller;
import com.example.book.model.Book;
import com.example.book.service.BookService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/books")
@RequiredArgsConstructor
public class BookController {
private final BookService bookService;
@GetMapping
public Flux<Book> getAllBooks() {
return bookService.getAllBooks();
}
@GetMapping("/{id}")
public Mono<Book> getBookById(@PathVariable String id) {
return bookService.getBookById(id);
}
@PostMapping
public Mono<Book> createBook(@RequestBody Book book) {
return bookService.saveBook(book);
}
@DeleteMapping("/{id}")
public Mono<Void> deleteBook(@PathVariable String id) {
return bookService.deleteBook(id);
}
}
βοΈ 6. application.yml
spring:
data:
mongodb:
uri: mongodb://localhost:27017/reactivebooks
server:
port: 8080
π 7. Main Application
package com.example.book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BookAppApplication {
public static void main(String[] args) {
SpringApplication.run(BookAppApplication.class, args);
}
}
π§ͺ Test Using cURL or Postman
β Create Book
POST /api/books
Content-Type: application/json
{
"title": "Spring WebFlux",
"author": "Nitesh Sah",
"price": 399.0
}
π Get All Books
GET /api/books
π Get Book By ID
GET /api/books/{id}
β Delete Book
DELETE /api/books/{id}
π§ Extras (optional)
- Add global error handling using
@ControllerAdvice - Add validation using
@Valid - Add DTO mapping using MapStruct or manually
- Add pagination and sorting
- Add unit and integration tests
For MySql
If you want to use MySQL with Spring WebFlux, the challenge is:
βοΈSpring Data R2DBC must be used instead of JPA because JPA (Hibernate) is blocking and doesn't support reactive programming.
β What to Use
| Layer | Technology |
|---|---|
| Database | MySQL |
| Reactive Driver | R2DBC MySQL driver |
| ORM/Access | Spring Data R2DBC |
| Reactive Framework | Spring WebFlux + Project Reactor |
ποΈ How to Build a Reactive Spring WebFlux + MySQL (R2DBC) Project
Weβll create a Reactive Book API with MySQL (using R2DBC).
π¦ pom.xml Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.8.2.RELEASE</version>
</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>
</dependencies>
𧬠Model Class
package com.example.book.model;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("books")
public class Book {
@Id
private Long id;
private String title;
private String author;
private Double price;
}
πΎ Repository
package com.example.book.repository;
import com.example.book.model.Book;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
public interface BookRepository extends ReactiveCrudRepository<Book, Long> {
}
π§ Service Layer
package com.example.book.service;
import com.example.book.model.Book;
import com.example.book.repository.BookRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
@RequiredArgsConstructor
public class BookService {
private final BookRepository bookRepository;
public Flux<Book> getAllBooks() {
return bookRepository.findAll();
}
public Mono<Book> getBookById(Long id) {
return bookRepository.findById(id);
}
public Mono<Book> createBook(Book book) {
return bookRepository.save(book);
}
public Mono<Void> deleteBook(Long id) {
return bookRepository.deleteById(id);
}
}
π Controller
package com.example.book.controller;
import com.example.book.model.Book;
import com.example.book.service.BookService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/api/books")
@RequiredArgsConstructor
public class BookController {
private final BookService bookService;
@GetMapping
public Flux<Book> getAllBooks() {
return bookService.getAllBooks();
}
@GetMapping("/{id}")
public Mono<Book> getBookById(@PathVariable Long id) {
return bookService.getBookById(id);
}
@PostMapping
public Mono<Book> createBook(@RequestBody Book book) {
return bookService.createBook(book);
}
@DeleteMapping("/{id}")
public Mono<Void> deleteBook(@PathVariable Long id) {
return bookService.deleteBook(id);
}
}
βοΈ application.yml
spring:
r2dbc:
url: r2dbc:mysql://localhost:3306/reactive_db
username: root
password: root
sql:
init:
platform: mysql
mode: always
logging:
level:
org.springframework.r2dbc: DEBUG
π§ͺ SQL Table Init
Put this in resources/schema.sql:
CREATE TABLE IF NOT EXISTS books (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
author VARCHAR(255),
price DOUBLE
);
β Spring Boot automatically runs
schema.sqlduring startup if configured.
π Main Class
@SpringBootApplication
public class BookAppApplication {
public static void main(String[] args) {
SpringApplication.run(BookAppApplication.class, args);
}
}
π§ Notes
- You canβt use JPA/Hibernate in reactive WebFlux apps.
- Use R2DBC repositories instead of Spring Data JPA.
- MySQL is supported through the R2DBC MySQL driver (non-blocking).
Would you like this turned into a downloadable project or pushed to GitHub for direct cloning?
Top comments (0)