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
4.0.0
com.example
reactive-book-app
1.0.0
org.springframework.boot
spring-boot-starter-webflux
org.springframework.boot
spring-boot-starter-data-mongodb-reactive
org.projectlombok
lombok
true
🧬 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
org.springframework.boot
spring-boot-starter-webflux
org.springframework.boot
spring-boot-starter-data-r2dbc
dev.miku
r2dbc-mysql
0.8.2.RELEASE
org.springframework.boot
spring-boot-starter-validation
org.projectlombok
lombok
true
🧬 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.sql
during 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?