Spring Boot WebFlux MongoDB CRUD REST API Tutorial

In this tutorial, you will learn how to build CRUD REST APIs using Spring Boot, Spring WebFlux, and MongoDB NoSQL database.

Before implementing this tutorial, make sure that you have installed MongoDB on your machine and MongoDB instance is running.

You may also be interested in Testing Spring WebFlux Reactive CRUD Rest APIs using WebTestClient.

Spring WebFlux Overview

Spring WebFlux is a non-blocking, reactive web framework for building reactive, scalable web applications. It is part of the Spring Framework, and it is built on top of Project Reactor, which is a reactive programming library for building asynchronous, non-blocking applications. 

Spring WebFlux provides support for both server-side and client-side components, and it can be used to build web applications that are reactive and scalable. It supports the creation of RESTful web services and web applications using the functional programming style, and it provides a reactive alternative to the Spring MVC web framework. 

Spring WebFlux uses a reactive stream-based model for handling requests and responses, which allows it to handle a high volume of requests with a small number of threads. Overall, Spring WebFlux is a powerful tool for building reactive, scalable web applications, and it is well-suited for use in high-concurrency environments.

Reactive MongoDB Driver

Reactive MongoDB is a driver for MongoDB that is designed to support the Reactive Streams specification. It allows developers to build reactive, non-blocking applications that can work with MongoDB in a more efficient and scalable manner. 

The Reactive MongoDB driver is built on top of the MongoDB Java driver, and it provides a reactive API that can be used to perform operations on MongoDB collections. It allows developers to work with MongoDB using a reactive programming model, which allows them to write asynchronous, non-blocking code that can handle a large number of concurrent operations. 

Reactive MongoDB supports all the core MongoDB features, including insert, update, delete, and find operations, and it also supports aggregation, map-reduce, and other advanced features. It can be used in conjunction with other reactive libraries, such as Project Reactor or RxJava, to build scalable, reactive applications that can work with MongoDB. 

Overall, Reactive MongoDB is a powerful tool for building reactive, scalable applications that can work with MongoDB, and it is well-suited for use in high-concurrency environments.

1. Create Spring Boot Application

Let's create a Spring boot application using Spring Intializr.

Refer to the below screenshot to enter the details while generating the Spring boot project using Spring Intializr:
Note that we are using Spring WebFlux, MongoDB Reactive, and Lombok libraries.

Here is the complete pom.xml file for your reference:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>net.javaguides</groupId>
	<artifactId>springboot-webflux-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot-webflux-demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<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>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

2. Project Structure

Refer to the below screenshot to create the packing or project structure for the application:

3. Configure MongoDB

You can configure MongoDB by simply adding the following property to the application.properties file:
spring.data.mongodb.uri=mongodb://localhost:27017/ems
Spring Boot will read this configuration on startup and automatically configure the data source.

4. Create Domain Class

Let's create an Employee MongoDB document and add the following content to it:
package net.javaguides.springbootwebfluxdemo.entity;

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

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Document(value = "employees")
public class Employee {

    @Id
    private String id;
    private String firstName;
    private String lastName;
    private String email;
}
Note that we have given MongoDB collection name - employees.

5. Creating Repository - EmployeeRepository

Next, we’re going to create the data access layer which will be used to access the MongoDB database.

Let's create an EmployeeRepository interface and add the following content to it:
package net.javaguides.springbootwebfluxdemo.repository;

import net.javaguides.springbootwebfluxdemo.entity.Employee;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

public interface EmployeeRepository extends ReactiveCrudRepository<Employee, String> {
}
The EmployeeRepository interface extends from ReactiveMongoRepository which exposes various CRUD methods on the Document. Spring Boot automatically plugs in an implementation of this interface called SimpleReactiveMongoRepository at runtime.

So you get all the CRUD methods on the Document readily available to you without needing to write any code.

6. Create EmployeeMapper - Map Entity to Dto and Vice Versa

Let's create EmployeeMapper class to map an entity to Dto and vice versa:
package net.javaguides.springbootwebfluxdemo.mapper;

import net.javaguides.springbootwebfluxdemo.dto.EmployeeDto;
import net.javaguides.springbootwebfluxdemo.entity.Employee;

public class EmployeeMapper {

    public static EmployeeDto mapToEmployeeDto(Employee employee){
        return new EmployeeDto(
                employee.getId(),
                employee.getFirstName(),
                employee.getLastName(),
                employee.getEmail()
        );
    }

    public static Employee mapToEmployee(EmployeeDto employeeDto){
        return new Employee(
                employeeDto.getId(),
                employeeDto.getFirstName(),
                employeeDto.getLastName(),
                employeeDto.getEmail()
        );
    }
}

7. Create Service Layer

EmployeeService Interface

Let's create an EmployeeService interface and add below CRUD methods to it:
package net.javaguides.springbootwebfluxdemo.service;

import net.javaguides.springbootwebfluxdemo.dto.EmployeeDto;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface EmployeeService {
    Mono<EmployeeDto> saveEmployee(EmployeeDto employeeDto);

    Mono<EmployeeDto> getEmployee(String employeeId);

    Flux<EmployeeDto> getAllEmployees();

    Mono<EmployeeDto> updateEmployee(EmployeeDto employeeDto, String employeeId);

    Mono<Void> deleteEmployee(String employeeId);
}

EmployeeServiceImpl class

Let's create EmployeeServiceImpl class that implements the EmployeeService interface and its methods:

package net.javaguides.springbootwebfluxdemo.service.impl;

import lombok.AllArgsConstructor;
import net.javaguides.springbootwebfluxdemo.dto.EmployeeDto;
import net.javaguides.springbootwebfluxdemo.entity.Employee;
import net.javaguides.springbootwebfluxdemo.mapper.EmployeeMapper;
import net.javaguides.springbootwebfluxdemo.repository.EmployeeRepository;
import net.javaguides.springbootwebfluxdemo.service.EmployeeService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
@AllArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeRepository employeeRepository;

    @Override
    public Mono<EmployeeDto> saveEmployee(EmployeeDto employeeDto) {
        Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
        Mono<Employee> savedEmployee = employeeRepository.save(employee);
        return savedEmployee
                .map((employeeEntity) -> EmployeeMapper.mapToEmployeeDto(employeeEntity));
    }

    @Override
    public Mono<EmployeeDto> getEmployee(String employeeId) {
        Mono<Employee> employeeMono = employeeRepository.findById(employeeId);
        return employeeMono.map((employee -> EmployeeMapper.mapToEmployeeDto(employee)));
    }

    @Override
    public Flux<EmployeeDto> getAllEmployees() {

        Flux<Employee> employeeFlux  = employeeRepository.findAll();
        return employeeFlux
                .map((employee) -> EmployeeMapper.mapToEmployeeDto(employee))
                .switchIfEmpty(Flux.empty());
    }

    @Override
    public Mono<EmployeeDto> updateEmployee(EmployeeDto employeeDto, String employeeId) {

        Mono<Employee> employeeMono = employeeRepository.findById(employeeId);

        return employeeMono.flatMap((existingEmployee) -> {
            existingEmployee.setFirstName(employeeDto.getFirstName());
            existingEmployee.setLastName(employeeDto.getLastName());
            existingEmployee.setEmail(employeeDto.getEmail());
            return employeeRepository.save(existingEmployee);
        }).map((employee -> EmployeeMapper.mapToEmployeeDto(employee)));
    }

    @Override
    public Mono<Void> deleteEmployee(String employeeId) {
        return employeeRepository.deleteById(employeeId);
    }
}

8. Create Controller Layer - Reactive REST APIs

Let’s write the APIs that will be exposed to the clients. Let's create an EmployeeController with the following contents:
package net.javaguides.springbootwebfluxdemo.controller;

import lombok.AllArgsConstructor;
import net.javaguides.springbootwebfluxdemo.dto.EmployeeDto;
import net.javaguides.springbootwebfluxdemo.service.EmployeeService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("api/employees")
@AllArgsConstructor
public class EmployeeController {

    private EmployeeService employeeService;

    @PostMapping
    @ResponseStatus(value = HttpStatus.CREATED)
    public Mono<EmployeeDto> saveEmployee(@RequestBody EmployeeDto employeeDto){
        return employeeService.saveEmployee(employeeDto);
    }

    @GetMapping("{id}")
    public Mono<EmployeeDto> getEmployee(@PathVariable("id") String employeeId){
        return employeeService.getEmployee(employeeId);
    }

    @GetMapping
    public Flux<EmployeeDto> getAllEmployees(){
        return employeeService.getAllEmployees();
    }

    @PutMapping("{id}")
    public Mono<EmployeeDto> updateEmployee(@RequestBody EmployeeDto employeeDto,
                                            @PathVariable("id") String employeeId){
        return employeeService.updateEmployee(employeeDto, employeeId);
    }

    @DeleteMapping("{id}")
    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    public Mono<Void> deleteEmployee(@PathVariable("id") String employeeId){
        return employeeService.deleteEmployee(employeeId);
    }
}
Note that all the controller endpoints return a Publisher in the form of a Flux or a Mono.

Testing Reactive CRUD REST APIs using Postman Client

Create Employee REST API: 

Java Guides - Create Employee REST API

Get Employee REST API:

Get All Employees REST API:


Update Employee REST API:

Delete Employee REST API:


Conclusion

In this tutorial, you learned how to build CRUD REST APIs using Spring Boot, Spring WebFlux, and MongoDB NoSQL database.


Check out all the Spring Boot tutorials at https://www.javaguides.net/p/spring-boot-tutorial.html

Comments