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?