Reactive Spring Boot Restful Web Service + Google Books API
Tags: java, springboot, reactive, restful
Published: 2019-11-20 18:54:48

The idea of this post, the first of a series, is to demonstrate how to write a Spring Boot Restful Web Service that access a remote API in a reactive way. My intention is to add more features, like caching and database, always using the reactive paradigm. In this first one, I will describe how you can start from zero to the point that you have a functional web service.

### Getting Started

I based this on the Spring tutorial [Building a Reactive RESTful Web Service](https://spring.io/guides/gs/reactive-rest-service/), and build it from the scratch, copying only the initial Gradle build file. The project structure is as this following image.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/iqxnjszs51332gvlyk9v.jpg)

I created a default Spring Boot Application class, the only difference is the *@ConponentScan* annotation that points to the base package of the application.

```java
package dev.alexladeira.springboot.reactive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"dev.alexladeira.springboot.reactive"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
```
Now the game begins!

### Writing the Router class

Thought the Router class I put the route that you going to expose, this is a simple class that create the link between the service that I was exposing and the handler that I create to handle the request.

```java
package dev.alexladeira.springboot.reactive.routes;

import dev.alexladeira.springboot.reactive.handlers.GoogleBooksHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
@ComponentScan({"handlers"})
public class SearchRouter {
    @Bean
    public RouterFunction<ServerResponse> search(GoogleBooksHandler googleBooksHandler) {
        return RouterFunctions.route(RequestPredicates.GET("/search"), googleBooksHandler::search);
    }
}
```
 Until now I did not need to use [Spring Reactor](https://projectreactor.io/), this changed when I wrote the handler.

### Writing the Handler and The Service classes

I created the handler to handle the request, call the external resource and create a response. Here I got the *searchTerm* from the request object and pass it to the service that is responsible for returning some information about the books that has the word that I passed as a parameter, in this example I was using the [Google Books API](https://developers.google.com/books).

```java
package dev.alexladeira.springboot.reactive.handlers;

import dev.alexladeira.springboot.reactive.domain.google.GoogleBook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import dev.alexladeira.springboot.reactive.services.GoogleBooksService;

import java.util.ArrayList;
import java.util.List;

@Component
@ComponentScan({"services"})
public class GoogleBooksHandler {

    @Autowired
    private GoogleBooksService googleBooksService;

    public Mono<ServerResponse> search(ServerRequest request) {
        String searchTerm = request.queryParam("searchTerm").orElse(null);
        return searchTerm != null ? ServerResponse.ok().body(BodyInserters.fromPublisher(this.googleBooksService.getBooksBy(searchTerm).reduce(new ArrayList<GoogleBook>(), (list, googleBookServiceResponse) -> {
            list.addAll(googleBookServiceResponse.items);
            return list;
        }), List.class)) : ServerResponse.badRequest().build();
    }
}
```
The job of the search method is just get the books from the Google Books API, reduce then to a list of *GoogleBooks* type, a java class that has only the info that I want the service to return, and create the response. If the service receive and empty call (without any parameters), then an error is thrown.

The service class is as follow, a call to the API via a *WebClient* object

```java
package dev.alexladeira.springboot.reactive.services;

import dev.alexladeira.springboot.reactive.domain.google.GoogleBookServiceResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

@Service
public class GoogleBooksService implements GenericService<GoogleBookServiceResponse> {
    private WebClient webClient = WebClient.builder().baseUrl("https://content.googleapis.com").build();

    @Override
    public Flux<GoogleBookServiceResponse> getBooksBy(String searchTerm) {
        return this.webClient.get().uri(uriBuilder -> uriBuilder
                .path("/books/v1/volumes")
                .queryParam("q", searchTerm)
                .queryParam("maxResults", MAX_RESULTS)
                .build()).retrieve().bodyToFlux(GoogleBookServiceResponse.class).timeout(TIMEOUT);
    }
}
```
This class extends *GenericService*, a class created to concentrated some information that is going to be used by the others services that I will create in the future, the constants MAX_RESULTS and TIMEOUT.

### Conclusion... for now

It's time start the server and see the results, point your browser to [http://localhost:8080](http://localhost:8080/search?searchTerm=springboot), if everything went well, you will see this:
```javascript
[{"volumeInfo":{"title":"Learning Spring Boot 2.0","authors":["Greg L. Turnquist"],"printType":"BOOK"}},{"volumeInfo":{"title":"Spring Boot 2.0 Projects","authors":["Mohamed Shazin Sadak
ath"],"printType":"BOOK"}},{"volumeInfo":{"title":"Spring: Microservices with Spring Boot","authors":["Ranga Rao Karanam"],"printType":"BOOK"}},{"volumeInfo":{"title":"Pro Spring Boot","
authors":["Felipe Gutierrez"],"printType":"BOOK"}},{"volumeInfo":{"title":"Mastering Spring Boot 2.0","authors":["Dinesh Rajput"],"printType":"BOOK"}}]
```

The source code is at [github.com/alexladeira/gs-reactive-rest-service](https://github.com/alexladeira/gs-reactive-rest-service). I will follow this post with others, always trying to show what I've being learning in SpringBoot topic.