Spring Boot WebFlux Annotation Example

In this tutorial, we’ll show you how to create the REST endpoints using WebFlux Mono and Flux.

Product.java

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

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Document
public class Product {
@Id
private String id;
private String name;
private Double price;

public Product(String name, Double price) {
this.name = name;
this.price = price;
}
}

ProductEvent.java

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

@AllArgsConstructor
@NoArgsConstructor
@Data
public class ProductEvent {
private Long eventId;
private String eventType;
}

ProductRepository.java

import com.wiredbraincoffee.productapiannotation.model.Product;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

public interface ProductRepository extends ReactiveMongoRepository<Product, String> {
}

ProductController.java

import com.wiredbraincoffee.productapiannotation.model.Product;
import com.wiredbraincoffee.productapiannotation.model.ProductEvent;
import com.wiredbraincoffee.productapiannotation.repository.ProductRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;

@RestController
@RequestMapping("/products")
public class ProductController {

private ProductRepository repository;

public ProductController(ProductRepository repository) {
this.repository = repository;
}

@GetMapping
public Flux<Product> getAllProducts() {
return repository.findAll();
}

@GetMapping("{id}")
public Mono<ResponseEntity<Product>> getProduct(@PathVariable String id) {
return repository.findById(id)
.map(product -> ResponseEntity.ok(product))
.defaultIfEmpty(ResponseEntity.notFound().build());
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<Product> saveProduct(@RequestBody Product product) {
return repository.save(product);
}

@PutMapping("{id}")
public Mono<ResponseEntity<Product>> updateProduct(@PathVariable(value = "id") String id,
@RequestBody Product product) {
return repository.findById(id)
.flatMap(existingProduct -> {
existingProduct.setName(product.getName());
existingProduct.setPrice(product.getPrice());
return repository.save(existingProduct);
})
.map(updateProduct -> ResponseEntity.ok(updateProduct))
.defaultIfEmpty(ResponseEntity.notFound().build());
}

@DeleteMapping("{id}")
public Mono<ResponseEntity<Void>> deleteProduct(@PathVariable(value = "id") String id) {
return repository.findById(id)
.flatMap(existingProduct ->
repository.delete(existingProduct)
.then(Mono.just(ResponseEntity.ok().<Void>build()))
)
.defaultIfEmpty(ResponseEntity.notFound().build());
}

@DeleteMapping
public Mono<Void> deleteAllProducts() {
return repository.deleteAll();
}

@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ProductEvent> getProductEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(val ->
new ProductEvent(val, "Product Event")
);
}
}

ProductApiAnnotationApplication.java

import com.wiredbraincoffee.productapiannotation.model.Product;
import com.wiredbraincoffee.productapiannotation.repository.ProductRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import reactor.core.publisher.Flux;

@SpringBootApplication
public class ProductApiAnnotationApplication {

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

@Bean
CommandLineRunner init(ReactiveMongoOperations operations, ProductRepository repository) {
return args -> {
Flux<Product> productFlux = Flux.just(
new Product("Big Latte", 2.99),
new Product("Big Decaf", 2.49),
new Product("Green Tea", 1.99)
)
.flatMap(repository::save);

productFlux
.thenMany(repository.findAll())
.subscribe(System.out::println);

/*operations.collectionExists(Product.class)
.flatMap(exists -> exists ? operations.dropCollection(Product.class) : Mono.just(exists))
.thenMany(v -> operations.createCollection(Product.class))
.thenMany(productFlux)
.thenMany(repository.findAll())
.subscribe(System.out::println);*/
};
}
}

Run the application and see the console logs

Product(id=6093af835d84f77d9cbf7548, name=Big Decaf, price=2.49)
Product(id=6093af835d84f77d9cbf7549, name=Green Tea, price=1.99)
Product(id=6093af835d84f77d9cbf7547, name=Big Latte, price=2.99)
Product(id=60c844e00506f75a8032223c, name=Big Decaf, price=2.49)
Product(id=60c844e00506f75a8032223d, name=Green Tea, price=1.99)
Product(id=60c844e00506f75a8032223b, name=Big Latte, price=2.99)
Product(id=60c852270506f77cc88d16d6, name=Big Latte, price=2.99)
Product(id=60c852270506f77cc88d16d8, name=Green Tea, price=1.99)
Product(id=60c852270506f77cc88d16d7, name=Big Decaf, price=2.49)

curl — location — request GET ‘http://localhost:8080/products'

[
{
"id": "6093af835d84f77d9cbf7548",
"name": "Big Decaf",
"price": 2.49
},
{
"id": "6093af835d84f77d9cbf7549",
"name": "Green Tea",
"price": 1.99
},
{
"id": "6093af835d84f77d9cbf7547",
"name": "Big Latte",
"price": 2.99
},
{
"id": "60c844e00506f75a8032223c",
"name": "Big Decaf",
"price": 2.49
},
{
"id": "60c844e00506f75a8032223d",
"name": "Green Tea",
"price": 1.99
},
{
"id": "60c844e00506f75a8032223b",
"name": "Big Latte",
"price": 2.99
},
....
....
]

GET — > http://localhost:8080/products/6093af835d84f77d9cbf7548

{
"id": "6093af835d84f77d9cbf7548",
"name": "Big Decaf",
"price": 2.49
}

You can also make the POST, DELETE and DELETE by Id etc