DEV Community

Alexandre Ladeira
Alexandre Ladeira

Posted on

2 2

Reactive Spring Boot Restful Web Service + Google Books API

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, and build it from the scratch, copying only the initial Gradle build file. The project structure is as this following image.

Alt Text

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

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Until now I did not need to use Spring Reactor, 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.

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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, if everything went well, you will see this:

[{"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"}}]
Enter fullscreen mode Exit fullscreen mode

The source code is at 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.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay