Spring Boot Reactive MongoDB CRUD Example - WebFlux

In this tutorial, we will learn how to create a Spring boot project with CRUD operations using Reactive Programming through Spring Data Reactive Repositories with MongoDB.
Source code of this tutorial is available on our GitHub repository at https://github.com/sourcecodeexamples/spring-reactive-mongo-crud
In order to use Reactive MongoDB, we need to add the dependency to our pom.xml.

We'll also add an embedded MongoDB for testing:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
    </dependency>
    <dependency>
        <groupId>de.flapdoodle.embed</groupId>
        <artifactId>de.flapdoodle.embed.mongo</artifactId>
        <scope>test</scope>
    </dependency>
Let's create a step-by-step Spring boot project from scratch that uses Reactive MongoDB.

1. Create Spring Boot Project

We’ll use Spring initializr web tool to bootstrap our application. 

Go to http://start.spring.io

Select Java in the language section.

Artifact: spring-boot-crud-example

Dependencies: Reactive, Lombok, JPA, and Embedded Mongo Database.

Package name: com.springboot.reactive

Click Generate to generate and download the project.

Once the project is generated, unzip it and import it into your favorite IDE.

2. Maven Dependencies

Open the pom.xml file and confirm below dependencies are present:

                <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>de.flapdoodle.embed</groupId>
			<artifactId>de.flapdoodle.embed.mongo</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>

3. Configure MongoDB

Let's first create a products database in the MongoDB server.

Then, open the application.yml file and add the following code to it:
spring:
  data:
    mongodb:
      database: Productsdb
      host: localhost
      port: 27017


server:
  port: 9292

4. Create Model Layer

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "products")
public class Product {
    @Id
    private String id;
    private String name;
    private int qty;
    private double price;
}

5. Create Repository Layer

import com.springboot.reactive.dto.ProductDto;
import com.springboot.reactive.entity.Product;
import org.springframework.data.domain.Range; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.stereotype.Repository; import reactor.core.publisher.Flux; @Repository public interface ProductRepository extends ReactiveMongoRepository<Product,String> { Flux<ProductDto> findByPriceBetween(Range<Double> priceRange); }

6. Create Service Layer

import com.springboot.reactive.dto.ProductDto;
import com.springboot.reactive.entity.Product;
import com.springboot.reactive.repository.ProductRepository;
import com.springboot.reactive.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Range; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service public class ProductService { @Autowired private ProductRepository repository; public Flux<ProductDto> getProducts(){ return repository.findAll().map(AppUtils::entityToDto); } public Mono<ProductDto> getProduct(String id){ return repository.findById(id).map(AppUtils::entityToDto); } public Flux<ProductDto> getProductInRange(double min,double max){ return repository.findByPriceBetween(Range.closed(min,max)); } public Mono<ProductDto> saveProduct(Mono<ProductDto> productDtoMono){ System.out.println("service method called ..."); return productDtoMono.map(AppUtils::dtoToEntity) .flatMap(repository::insert) .map(AppUtils::entityToDto); } public Mono<ProductDto> updateProduct(Mono<ProductDto> productDtoMono,String id){ return repository.findById(id) .flatMap(p->productDtoMono.map(AppUtils::dtoToEntity) .doOnNext(e->e.setId(id))) .flatMap(repository::save) .map(AppUtils::entityToDto); } public Mono<Void> deleteProduct(String id){ return repository.deleteById(id); } }

7. Create DTO Class

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductDto {

    private String id;
    private String name;
    private int qty;
    private double price;
}

8. DTO to Entity Converter Class

import com.springboot.reactive.dto.ProductDto;
import com.springboot.reactive.entity.Product;
import org.springframework.beans.BeanUtils; public class AppUtils { public static ProductDto entityToDto(Product product) { ProductDto productDto = new ProductDto(); BeanUtils.copyProperties(product, productDto); return productDto; } public static Product dtoToEntity(ProductDto productDto) { Product product = new Product(); BeanUtils.copyProperties(productDto, product); return product; } }

9. Controller Layer

import com.springboot.reactive.dto.ProductDto;
import com.springboot.reactive.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping("/products") public class ProductController { @Autowired private ProductService service; @GetMapping public Flux<ProductDto> getProducts(){ return service.getProducts(); } @GetMapping("/{id}") public Mono<ProductDto> getProduct(@PathVariable String id){ return service.getProduct(id); } @GetMapping("/product-range") public Flux<ProductDto> getProductBetweenRange(@RequestParam("min") double min, @RequestParam("max")double max){ return service.getProductInRange(min,max); } @PostMapping public Mono<ProductDto> saveProduct(@RequestBody Mono<ProductDto> productDtoMono){ System.out.println("controller method called ..."); return service.saveProduct(productDtoMono); } @PutMapping("/update/{id}") public Mono<ProductDto> updateProduct(@RequestBody Mono<ProductDto> productDtoMono,@PathVariable String id){ return service.updateProduct(productDtoMono,id); } @DeleteMapping("/delete/{id}") public Mono<Void> deleteProduct(@PathVariable String id){ return service.deleteProduct(id); } }

10. Run Spring Boot Application

We’ve successfully built all the APIs for our application. Let’s now run the app and test the APIs.

Just go to the root directory of the application and type the following command to run it -

$ mvn spring-boot:run

Spring Boot application running on a server on port 9292.

11. REST APIs Details for Testing

Use below endpoint URLs for testing CRUD REST APIs.

Create new Product:

URL - http ://localhost:8080/products
HTTP Method - POST

Get All Products:

URL - http ://localhost:8080/products
HTTP Method - GET

Update existing Product:

URL - http ://localhost:8080/products/update/{id}
HTTP Method - PUT

Delete existing Product:

URL - http ://localhost:8080/products/delete/{id}
HTTP Method - DELETE

12. Source Code on GitHub

Source code of this tutorial is available on our GitHub repository at https://github.com/sourcecodeexamples/spring-reactive-mongo-crud

Comments