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