Spring Boot, MongoDB, Reactive CRUD example

In this tutorial, we’re gonna build a Spring Boot MongoDB Reactive example – CRUD application that uses Maven, Spring Data Reactive MongoDB to interact with MongoDB database and Spring WebFlux for Reactive Rest API. You’ll know:

  • How to configure Spring Data to work with MongoDB Database
  • How to define Data Model and Repository interface
  • Way to create Spring Rest Controller to process HTTP requests
  • Way to use Spring Data Reactive MongoDB to interact with Database

More Practice:
Spring WebFlux File upload example
Spring Boot + GraphQL + MongoDB example with Spring Data & graphql-java
Spring Boot, MongoDB: JWT Authentication with Spring Security
Spring Boot MongoDB Pagination example with Spring Data
– Dockerize: Docker Compose: MongoDB and Spring Boot example

Exception Handling:
Spring Boot @ControllerAdvice & @ExceptionHandler example
@RestControllerAdvice example in Spring Boot

Fullstack:
Angular 8 + Spring Boot + MongoDB example
Angular 10 + Spring Boot + MongoDB example
Angular 11 + Spring Boot + MongoDB example
Angular 12 + Spring Boot + MongoDB example
Angular 13 + Spring Boot + MongoDB example
Angular 14 + Spring Boot + MongoDB example
Angular 15 + Spring Boot + MongoDB example
Angular 16 + Spring Boot + MongoDB example
React + Spring Boot + MongoDB example
Vue.js + Spring Boot + MongoDB example


Overview of Spring MongoDB Reactive example

We will build a Spring Boot, MongoDB, Reactive example – Rest CRUD API for a Tutorial application in that:

  • Each Tutorial has id, title, description, published status.
  • Apis help to create, retrieve, update, delete Tutorials.
  • Apis also support custom finder methods such as find by published status or by title.

These are APIs that we need to provide:

Methods Urls Actions
POST /api/tutorials create new Tutorial
GET /api/tutorials retrieve all Tutorials
GET /api/tutorials/:id retrieve a Tutorial by :id
PUT /api/tutorials/:id update a Tutorial by :id
DELETE /api/tutorials/:id delete a Tutorial by :id
DELETE /api/tutorials delete all Tutorials
GET /api/tutorials/published find all published Tutorials
GET /api/tutorials?title=[keyword] find all Tutorials which title contains keyword

Technology

  • Java 11/17
  • Spring Boot 3 (with Spring WebFlux, Spring Data MongoDB Reactive)
  • MongoDB
  • Maven 3.6.1

Project Structure

spring-boot-mongodb-reactive-example-project

Let me explain it briefly.

Tutorial data model class.
TutorialRepository is an interface that extends ReactiveMongoRepository to interact with the database. It is autowired in TutorialService.
TutorialService is a service component that uses TutorialRepository and provides CRUD methods and custom finder methods for TutorialController.
TutorialController is a RestController which has request mapping methods for RESTful requests such as: getAllTutorials, createTutorial, updateTutorial, deleteTutorial, findByPublished
– Configuration for Spring Data MongoDB is in application.properties.
schema.sql has SQL statement for initializing database table.
pom.xml contains dependencies for Spring Boot, WebFlux, Spring Data MongoDB Reactive.

Create & Setup Spring Boot project

Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.

Then open pom.xml and add these dependencies:

<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>
</dependencies>

Configure the Spring Data MongoDB database

Under src/main/resources folder, open application.properties and write these lines.

spring.data.mongodb.uri=mongodb://localhost:27017/bezkoder_db

The connection URL is set to mongodb://localhost:27017/[database-name], which indicates that you are using a MongoDB database located at localhost on port 27017.

Define Data Model

Our Data model is Tutorial with four fields: id, title, description, published.
In model package, we define Tutorial class.

model/Tutorial.java

package com.bezkoder.spring.mongodb.reactive.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class Tutorial {

  @Id
  private String id;

  private String title;

  private String description;

  private boolean published;

  public Tutorial() {

  }

  public Tutorial(String title, String description, boolean published) {
    this.title = title;
    this.description = description;
    this.published = published;
  }

  // getters and setters

  @Override
  public String toString() {
    return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
  }
}

Create Repository Interface

Let’s create a repository to interact with Tutorials from the MongoDB database.
In repository package, create TutorialRepository interface that extends ReactiveMongoRepository (which extends Spring Data Reactive ReactiveCrudRepository).

repository/TutorialRepository.java

package com.bezkoder.spring.mongodb.reactive.repository;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;

import com.bezkoder.spring.mongodb.reactive.model.Tutorial;

import reactor.core.publisher.Flux;

@Repository
public interface TutorialRepository extends ReactiveMongoRepository<Tutorial, String> {
  Flux<Tutorial> findByPublished(boolean published);

  Flux<Tutorial> findByTitleContaining(String title);
}

Now we can use ReactiveMongoRepository and ReactiveCrudRepository methods: insert() / save(), findById(), findAll(), count(), delete(), deleteById(), deleteAll()… without implementing these methods.

We also define custom finder methods:
findByPublished(): returns all Tutorials with published having value as input published.
findByTitleContaining(): returns all Tutorials which title contains input title.

The implementation is plugged in by Spring Data MongoDB automatically.

Create Data Service

Let’s create a service that uses TutorialRepository to implement CRUD Operations and custom finder methods.
In service package, create TutorialService.

service/TutorialService.java

package com.bezkoder.spring.mongodb.reactive.service;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.bezkoder.spring.mongodb.reactive.model.Tutorial;
import com.bezkoder.spring.mongodb.reactive.repository.TutorialRepository;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class TutorialService {

  @Autowired
  TutorialRepository tutorialRepository;

  public Flux<Tutorial> findAll() {
    return tutorialRepository.findAll();
  }

  public Flux<Tutorial> findByTitleContaining(String title) {
    return tutorialRepository.findByTitleContaining(title);
  }

  public Mono<Tutorial> findById(String id) {
    return tutorialRepository.findById(id);
  }

  public Mono<Tutorial> save(Tutorial tutorial) {
    return tutorialRepository.save(tutorial);
  }

  public Mono<Tutorial> update(String id, Tutorial tutorial) {
    return tutorialRepository.findById(id).map(Optional::of).defaultIfEmpty(Optional.empty())
        .flatMap(optionalTutorial -> {
          if (optionalTutorial.isPresent()) {
            tutorial.setId(id);
            return tutorialRepository.save(tutorial);
          }

          return Mono.empty();
        });
  }

  public Mono<Void> deleteById(String id) {
    return tutorialRepository.deleteById(id);
  }

  public Mono<Void> deleteAll() {
    return tutorialRepository.deleteAll();
  }

  public Flux<Tutorial> findByPublished(boolean isPublished) {
    return tutorialRepository.findByPublished(isPublished);
  }
}

TutorialService class is annotated with @Service to indicate that it is a service component. Spring will create a singleton bean for this class and manage its lifecycle.
– We use @Autowired to inject TutorialRepository bean to local variable.

Create Spring Reactive Rest API Controller

Finally, we create a controller that provides APIs for creating, retrieving, updating, deleting and finding Tutorials.

controller/TutorialController.java

package com.bezkoder.spring.mongodb.reactive.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.bezkoder.spring.mongodb.reactive.model.Tutorial;
import com.bezkoder.spring.mongodb.reactive.service.TutorialService;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {

  @Autowired
  TutorialService tutorialService;

  @GetMapping("/tutorials")
  @ResponseStatus(HttpStatus.OK)
  public Flux<Tutorial> getAllTutorials(@RequestParam(required = false) String title) {
    if (title == null)
      return tutorialService.findAll();
    else
      return tutorialService.findByTitleContaining(title);
  }

  @GetMapping("/tutorials/{id}")
  @ResponseStatus(HttpStatus.OK)
  public Mono<Tutorial> getTutorialById(@PathVariable("id") String id) {
    return tutorialService.findById(id);
  }

  @PostMapping("/tutorials")
  @ResponseStatus(HttpStatus.CREATED)
  public Mono<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
    return tutorialService.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
  }

  @PutMapping("/tutorials/{id}")
  @ResponseStatus(HttpStatus.OK)
  public Mono<Tutorial> updateTutorial(@PathVariable("id") String id, @RequestBody Tutorial tutorial) {
    return tutorialService.update(id, tutorial);
  }

  @DeleteMapping("/tutorials/{id}")
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public Mono<Void> deleteTutorial(@PathVariable("id") String id) {
    return tutorialService.deleteById(id);
  }

  @DeleteMapping("/tutorials")
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public Mono<Void> deleteAllTutorials() {
    return tutorialService.deleteAll();
  }

  @GetMapping("/tutorials/published")
  @ResponseStatus(HttpStatus.OK)
  public Flux<Tutorial> findByPublished() {
    return tutorialService.findByPublished(true);
  }
}

@CrossOrigin is for configuring allowed origins.
@RestController annotation is used to define a controller and to indicate that the return value of the methods should be be bound to the web response body.
@RequestMapping("/api") declares that all Apis’ url in the controller will start with /api.
– We use @Autowired to inject TutorialService bean to local variable.

Enable WebFlux

We use @EnableWebFlux to enable support for reactive web application using the Spring WebFlux framework.

SpringBootMongodbReactiveApplication.java

package com.bezkoder.spring.mongodb.reactive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.reactive.config.EnableWebFlux;

@EnableWebFlux
@SpringBootApplication
public class SpringBootMongodbReactiveApplication {

  public static void main(String[] args) {
    SpringApplication.run(SpringBootMongodbReactiveApplication.class, args);
  }
}

Run & Check

Run Spring Boot application with Maven command: mvn spring-boot:run.

Create some Tutorial documents:

spring-boot-mongodb-reactive-example-tutorial-create

Check MongoDB database collection:

spring-boot-mongodb-reactive-example-tutorial-collection

Retrieve all Tutorial documents:

spring-boot-mongodb-reactive-example-tutorial-retrieve

Update some Tutorials:

spring-boot-mongodb-reactive-example-tutorial-update-document

Check MongoDB database collection:

spring-boot-mongodb-reactive-example-tutorial-update-collection

Retrieve a Tutorial document by Id:

spring-boot-mongodb-reactive-example-tutorial-retrieve-one

Filter published Tutorials:

spring-boot-mongodb-reactive-example-tutorial-filter-document

Find all Tutorial documents which title contains a certain string:

spring-boot-mongodb-reactive-example-tutorial-search-document

Delete a Tutorial document by id:

spring-boot-mongodb-reactive-example-tutorial-delete-document

Check MongoDB database collection:

spring-boot-mongodb-reactive-example-tutorial-delete-check

Delete all Tutorials and check:

spring-boot-mongodb-reactive-example-tutorial-collection-delete

You can also check this Spring Boot App with Client in one of these posts:

Conclusion

Today we’ve built a Spring Data MongoDB Reactive example in Spring Boot successfully using WebFlux Framework for Rest API, Spring Data Reactive MongoDB for CRUD and custom finder methods.

Happy learning! See you again.

Source Code

You can find the complete source code for this tutorial on Github.

Further Reading

Fullstack CRUD App:
Angular 8 + Spring Boot + MongoDB example
Angular 10 + Spring Boot + MongoDB example
Angular 11 + Spring Boot + MongoDB example
Angular 12 + Spring Boot + MongoDB example
Angular 13 + Spring Boot + MongoDB example
Angular 14 + Spring Boot + MongoDB example
Angular 15 + Spring Boot + MongoDB example
Angular 16 + Spring Boot + MongoDB example
React + Spring Boot + MongoDB example
Vue.js + Spring Boot + MongoDB example

More Practice:
Spring WebFlux File upload example
Spring Boot + GraphQL + MongoDB example with Spring Data & graphql-java
Spring Boot, MongoDB: JWT Authentication with Spring Security
Spring Boot MongoDB Pagination example with Spring Data
– Dockerize: Docker Compose: MongoDB and Spring Boot example

Exception Handling:
Spring Boot @ControllerAdvice & @ExceptionHandler example
@RestControllerAdvice example in Spring Boot