DEV Community

Dev Cookies
Dev Cookies

Posted on

Spring WebFlux Reactive REST API project

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
Enter fullscreen mode Exit fullscreen mode

🧩 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>
Enter fullscreen mode Exit fullscreen mode

🧬 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;
}
Enter fullscreen mode Exit fullscreen mode

πŸ’Ύ 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> {
}
Enter fullscreen mode Exit fullscreen mode

πŸ”§ 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

🌐 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

βš™οΈ 6. application.yml

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/reactivebooks
server:
  port: 8080
Enter fullscreen mode Exit fullscreen mode

πŸš€ 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Test Using cURL or Postman

βž• Create Book

POST /api/books
Content-Type: application/json

{
  "title": "Spring WebFlux",
  "author": "Nitesh Sah",
  "price": 399.0
}
Enter fullscreen mode Exit fullscreen mode

πŸ“š Get All Books

GET /api/books
Enter fullscreen mode Exit fullscreen mode

πŸ“– Get Book By ID

GET /api/books/{id}
Enter fullscreen mode Exit fullscreen mode

❌ Delete Book

DELETE /api/books/{id}
Enter fullscreen mode Exit fullscreen mode

🧠 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>
Enter fullscreen mode Exit fullscreen mode

🧬 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;
}
Enter fullscreen mode Exit fullscreen mode

πŸ’Ύ 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> {
}
Enter fullscreen mode Exit fullscreen mode

πŸ”§ 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

🌐 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

βš™οΈ 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
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ 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
);
Enter fullscreen mode Exit fullscreen mode

βœ… 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

🧠 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)