Enterprise Java

Build Reactive REST APIs with Spring WebFlux – Part3

In continuation of the last article, we will see an application to expose reactive REST APIs. In this application, we used,

  • Spring Boot with WebFlux
  • Spring Data for Cassandra with Reactive Support
  • Cassandra Database

Below is the high-level architecture of the application.

Let us look at the build.gradle file to see what dependencies are included to work with the Spring WebFlux.

plugins {
	id 'org.springframework.boot' version '2.2.6.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
}

group = 'org.smarttechie'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-cassandra-reactive'
	implementation 'org.springframework.boot:spring-boot-starter-webflux'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	testImplementation 'io.projectreactor:reactor-test'
}

test {
	useJUnitPlatform()
}

In this application, I have exposed the below mentioned APIs. You can download the source code from GitHub.

EndpointURIResponse
Create a Product/productCreated product as Mono
All products/productsreturns all products as Flux
Delate a product/product/{id}Mono
Update a product/product/{id}Updated product as Mono

The product controller code with all the above endpoints is given below.

package org.smarttechie.controller;

import org.smarttechie.model.Product;
import org.smarttechie.repository.ProductRepository;
import org.smarttechie.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
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;

@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    /**
     * This endpoint allows to create a product.
     * @param product - to create
     * @return - the created product
     */
    @PostMapping("/product")
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Product> createProduct(@RequestBody Product product){
        return productService.save(product);
    }

    /**
     * This endpoint gives all the products
     * @return - the list of products available
     */
    @GetMapping("/products")
    public Flux<Product> getAllProducts(){
        return productService.getAllProducts();
    }

    /**
     * This endpoint allows to delete a product
     * @param id - to delete
     * @return
     */
    @DeleteMapping("/product/{id}")
    public Mono<Void> deleteProduct(@PathVariable int id){
        return productService.deleteProduct(id);
    }

    /**
     * This endpoint allows to update a product
     * @param product - to update
     * @return - the updated product
     */
    @PutMapping("product/{id}")
    public Mono<ResponseEntity<Product>> updateProduct(@RequestBody Product product){
        return productService.update(product);
    }
}

As we are building reactive APIs, we can build APIs with a functional style programming model without using RestController. In this case, we need to have a router and a handler component as shown below.

package org.smarttechie.router;

import org.smarttechie.handler.ProductHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
@Configuration
public class ProductRouter {

    /**
     * The router configuration for the product handler.
     * @param productHandler
     * @return
     */
    @Bean
    public RouterFunction<ServerResponse> productsRoute(ProductHandler productHandler){

        return RouterFunctions
                .route(GET("/products").and(accept(MediaType.APPLICATION_JSON))
                        ,productHandler::getAllProducts)
                .andRoute(POST("/product").and(accept(MediaType.APPLICATION_JSON))
                        ,productHandler::createProduct)
                .andRoute(DELETE("/product/{id}").and(accept(MediaType.APPLICATION_JSON))
                        ,productHandler::deleteProduct)
                .andRoute(PUT("/product/{id}").and(accept(MediaType.APPLICATION_JSON))
                        ,productHandler::updateProduct);
    }
}
package org.smarttechie.handler;

import org.smarttechie.model.Product;
import org.smarttechie.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;

@Component
public class ProductHandler {
    @Autowired
    private ProductService productService;
    static Mono<ServerResponse> notFound = ServerResponse.notFound().build();

    /**
     * The handler to get all the available products.
     * @param serverRequest
     * @return - all the products info as part of ServerResponse
     */
    public Mono<ServerResponse> getAllProducts(ServerRequest serverRequest) {

        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(productService.getAllProducts(), Product.class);

    }

    /**
     * The handler to create a product
     * @param serverRequest
     * @return - return the created product as part of ServerResponse
     */
    public Mono<ServerResponse> createProduct(ServerRequest serverRequest) {

        Mono<Product> productToSave = serverRequest.bodyToMono(Product.class);

        return productToSave.flatMap(product ->
                ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(productService.save(product), Product.class));

    }

    /**
     * The handler to delete a product based on the product id.
     * @param serverRequest
     * @return - return the deleted product as part of ServerResponse
     */
    public Mono<ServerResponse> deleteProduct(ServerRequest serverRequest) {

        String id = serverRequest.pathVariable("id");
        Mono<Void> deleteItem = productService.deleteProduct(Integer.parseInt(id));

        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(deleteItem, Void.class);
    }

    /**
     * The handler to update a product.
     * @param serverRequest
     * @return - The updated product as part of ServerResponse
     */
    public Mono<ServerResponse> updateProduct(ServerRequest serverRequest) {
        return productService.update(serverRequest.bodyToMono(Product.class)).flatMap(product ->
                ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(fromObject(product)))
                .switchIfEmpty(notFound);
    }
}

So far, we have seen how to expose reactive REST APIs. With this implementation, I have done a simple benchmarking on reactive APIs versus the non-reactive APIs (built non-reactive APIs using Spring RestController) using Gatling. Below are the comparison metrics between the reactive and non-reactive APIs. This is not an extensive benchmarking. So, before adopting please make sure to do extensive benchmarking for your use case. 

The Gatling load test scripts are also available on GitHub for your reference. With this, I conclude the series of “Build Reactive REST APIs with Spring WebFlux“. We will meet on another topic. Till then, Happy Learning!!

Published on Java Code Geeks with permission by Siva Janapati, partner at our JCG program. See the original article here: Build Reactive REST APIs with Spring WebFlux – Part3

Opinions expressed by Java Code Geeks contributors are their own.

Siva Janapati

Siva Prasad Rao Janapati is an Architect. He has hands on experience on Java, JEE, Spring, Oracle Commerce, MOZU Commerce, Apache Solr, Apache Kafka, Node.js, JBoss, Hibernate, Memcached, MySql, Oracle, MongoDB, APIGEE, Cloud Native, BlockChain and other open source/enterprise technologies. He loves to explore new technologies and trends.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button