Consumindo apis externas com spring webflux
Published: 2020-08-06 23:10:56

Há algum tempo ajudando uma amiga com um teste para um processo seletivo, me deparei com o desafio de consumir apis externas. Desafio dado, porque não buscar algo novo e aprender algo diferente? Foi assim que escolhemos o webclient do Spring Webflux para a tarefa. 

Segundo a documentação do Spring, o Webflux é a stack reativa do Spring que foi adicionado a partir da versão 5 do framework que está presente na versão 2.0 ou superiores do Springboot. Com ele é possível fazer chamadas síncronas e assíncronas e tem sido utilizado em muitos projetos no mercado, inclusive no que eu trabalho atualmente! :)

Neste projeto vamos consumir [apis do star wars](https://swapi.dev/) a ideia é criar uma api de leilão para os veículos da franquia. Num primeiro momento vamos acessar os endpoints que retornam todos os veículos e os que devolvem os veículos individualmente. 
Partiu?

Vamos começar entendendo como disponibilizar essa ferramenta na nossa api. Eu prefiro utilizar o gradle como ferramenta de build e ele foi o escolhido para esse projeto, outro ponto importante, foi que utilizei o [spring initializr](initializr https://start.spring.io/) para criar o projeto, sendo assim foi preciso apenas adicionar a dependência do Spring Reactive Web. Caso prefira criar o projeto de outra maneira, pode adicionar a seguinte linha:
 `implementation 'org.springframework.boot:spring-boot-starter-webflux'` 
ao arquivo build.gradle. 

Com o webflux disponível, podemos criar nosso `webclient` que é quem vai orquestrar as nossas requisições a star wars api. Como se trata de uma biblioteca externa precisamos que o Spring possa tomar conta do seu ciclo de vida pra nós, e para isso a transformamos em um `bean`. Isso pode ser feito na classe que está anotada com o `@SpringBootApplication` como abaixo:
```Java
@SpringBootApplication
public class MyApplication {
  @Bean
  public WebClient webClient(WebClient.Builder builder) {
     return builder.baseUrl("https://swapi.dev/api/")
           .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
           .build();
  }
  public static void main(String[] args) {
     SpringApplication.run(MyApplication.class, args);
  }

}
```
Ou ainda criar uma classe de onde possamos configurar todos os beans da nossa aplicação, mantendo assim a classe com uma única responsabilidade, o que eu prefiro fazer. A minha ficou assim:

```Java
@Configuration
public class BeanConfiguration {

   private String starWarsUrl;

   public BeanConfiguration(StarWarsConfiguration starWarsConfiguration){
       this.starWarsUrl = starWarsConfiguration.starWarsBaseUrl;
   }

   @Bean
   public WebClient webClient(WebClient.Builder builder) {
       return builder.baseUrl(starWarsUrl)
               .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
               .build();
   }
}
```

Outra boa prática que gosto de seguir, é não manter informações que são dinâmicas 'chumbadas' no código, como é o caso da url base da api que queremos acessar. Para manter essas informações isoladas em um único local preferi guardar os dados no `application.properties` do meu projeto:

```Java
starwars.base-url = https://swapi.dev/api/
starwars.vehicles = vehicles/
starwars.vehicle = vehicles/{id}/
```

E para acessar esses dados, usei mais uma classe de configuração. Aqui, a única função dela é disponibilizar as informações do `application.properties` em variáveis.

```Java
@Configuration
@PropertySource("classpath:application.properties")
public class StarWarsConfiguration {

   @Value("${starwars.base-url}")
   public String starWarsBaseUrl;

   @Value("${starwars.vehicles}")
   public String starWarsVehicles;

   @Value("${starwars.vehicle}")
   public String starWarsVehicle;
}
```

Com todas as classes de configurações apresentadas, deixo uma imagem para que fique um pouco mais claro como elas se conversam.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0ga9zxidr65fb3inqfzn.png)


Partindo para as nossas requisições, a ideia foi criar um `component` cuja função seja apenas isso, acessar a api do star wars e retornar as informações que precisamos. Primeiro, disponibilizo através do construtor o bean do `webclient` para utilizar nas chamadas e as variáveis que contem as urls que serão buscadas.

```Java
@Component
public class StarWarsWebClient {

   private final String allVehiclesResource;
   private final String vehicleResource;
   private WebClient webClient;

   StarWarsWebClient(WebClient webClient, StarWarsConfiguration starWarsConfiguration) {
       this.webClient = webClient;
       this.allVehiclesResource = starWarsConfiguration.starWarsVehicles;
       this.vehicleResource = starWarsConfiguration.starWarsVehicle;
   }
```

Feito isso é só criar o método que efetivamente vai fazer a requisição. Neste caso buscaremos primeiro todos os veículos e aqui é preciso fazer uma ressalva, se a api externa retornasse apenas uma lista com todos veículos disponíveis `[{},{}]`, poderíamos utilizar um flux e o método ficaria mais ou menos assim:

```Java
public Flux<VehicleResponse> getAllVehicles() {
        Flux<VehicleResponse> response = webClient
.get() // metodo http utilizado na requisicao
                .uri(allVehiclesResource) //recurso a ser atingido encapsulado na variavel
                .retrieve() //executa a chamada
                .bodyToFlux(VehicleResponse.class); //transforma a resposta em um Flux do tipo VehicleResponse
        return response;
    }
```

No entanto a nossa api retorna um único objeto contendo em uma de suas propriedades uma lista de veículos `{[]}`, nesse caso o método utilizado foi o método a seguir, e por enquanto focaremos só no Mono:

```Java
public AllVehicleResponse getAllVehicles() {
   return webClient
           .get()
           .uri(allVehiclesResource)
           .retrieve()
           .bodyToMono(AllVehicleResponse.class)
           .block();
}
```

E para atingir os veículos individualmente, o processo é quase o mesmo:

```Java
public VehicleResponse getVehicle(Integer id) {
   return webClient
           .method(HttpMethod.GET)
           .uri(vehicleResource, id)
           .retrieve()
           .bodyToMono(VehicleResponse.class)
           .block();
}
```

A diferença está na chamada da uri, a variável `vehicleResource` contem o recurso seguido do `id` que está sendo buscado `vehicles/{id}/` e na frente dele, uma variável que de fato armazena esse id.

A partir daí qualquer classe service no nosso projeto pode acessar os caras que fazem essas requisições e trabalhar com seus dados. Como no exemplo abaixo:

```Java
@Service
public class VehiclesService {
   private final VehicleRepository vehicleRepository;
   private final StarWarsWebClient starWarsWebClient;

   public VehiclesService(VehicleRepository vehicleRepository, StarWarsWebClient starWarsWebClient) {
       this.vehicleRepository = vehicleRepository;
       this.starWarsWebClient = starWarsWebClient;
   }

   public AllVehicleResponse getVehicles() {
       return starWarsWebClient.getAllVehicles();
   }

   public VehicleResponse getVehicle(Integer id) {
       return starWarsWebClient.getVehicle(id);
   }
}
```

Fácil né? Nos próximos capítulos vamos explorar mais esses dados e montar nossa api de leilões! Quer ver o projeto no git? O link é esse [aqui](https://github.com/gleiceellen/auction). Espero que tenham gostado. 
Até!