🌌Guia Prático: Consumindo a API da NASA para Dados Espaciais
- #Java
- #Programação para Internet
- #Spring
- #API Rest
Introdução
No mundo da ciência espacial, a NASA disponibiliza diversos dados valiosos sobre objetos próximos à Terra, como asteroides e cometas, por meio da API NeoWS (Near-Earth Object Web Service). Neste artigo, vamos explorar como construir uma API cliente utilizando o Spring Boot com Spring WebFlux para interagir com a API NeoWS. O foco do artigo é mostrar como configurar e construir uma aplicação reativa, fazendo uso de WebClient e Mono para interagir com a API de maneira assíncrona.
Embora não abordemos a implementação da entidade Asteroid, apresentaremos os métodos necessários para interagir com a API NeoWS. Para mais detalhes sobre a API, você pode acessar o repositório desta implementação.
Para utilizar a API da NASA, você precisa de uma chave (API Key). O cadastro é rápido e gratuito, basta acessar api.nasa.gov e se registrar com seu e-mail. Nesse mesmo Link você encontrará a documentação da NeoWS e outras diversas API's, divirta-se!
Spring WebFlux: Por que escolhê-lo?
O Spring WebFlux é um módulo do Spring Framework voltado para a construção de aplicações reativas. Diferente do modelo tradicional, onde os componentes de uma aplicação funcionam de maneira síncrona e bloqueante, o WebFlux é baseado no paradigma reativo, permitindo que o servidor e o cliente operem de maneira não bloqueante e assíncrona. Isso torna o WebFlux uma excelente escolha quando a aplicação precisa lidar com grandes volumes de requisições ou quando é necessário realizar chamadas a APIs externas, como a NeoWS, de forma eficiente.
Estrutura do Projeto
A arquitetura do projeto foi pensada com base nos princípios de separação de responsabilidades. Veja os principais componentes:
Configuração do WebClient
Uma das partes cruciais na construção de um cliente para APIs externas é o WebClient. Essa classe fornece uma API fluente e reativa para realizar chamadas HTTP. Abaixo, apresentamos a configuração do WebClient, que é feita em uma classe de configuração Spring. Ao configurar o WebClient estamos ajustando as engrenagens internas de um cliente HTTP reativo para otimizar a comunicação da sua aplicação com outros serviços online. Isso envolve definir como as conexões são estabelecidas e reutilizadas, quais informações de cabeçalho são enviadas por padrão, como os dados são convertidos entre formatos, quanto tempo esperar por respostas, tudo para garantir uma interação eficiente, segura e robusta com o mundo da web.
Classe de Configuração WebClient:
@Configuration
public class WebClientConfig {
@Value("${api.key}")
private String apikey;
@Value("${nasa.api.base-url}")
private String baseUrl;
@Bean
public WebClient webClient() {
// Configurando timeouts
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.responseTimeout(Duration.ofSeconds(5))
.doOnConnected(connection ->
connection.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
// Filtro para adicionar a api_key como query param automaticamente
ExchangeFilterFunction apiKeyFilter = ExchangeFilterFunction.ofRequestProcessor(request -> {
URI updatedUri = UriComponentsBuilder.fromUri(request.url())
.queryParam("api_key", apikey)
.build(true)
.toUri();
ClientRequest updatedRequest = ClientRequest.from(request)
.url(updatedUri)
.build();
return reactor.core.publisher.Mono.just(updatedRequest);
});
// Construindo o WebClient
return WebClient.builder()
.baseUrl(baseUrl)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader("Content-Type", "application/json")
.defaultHeader("Accept", "application/json")
.filter(apiKeyFilter)
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(512 * 1024))
.build();
}
}
Explicação da Configuração
Configuração de Timeout: Utilizamos o HttpClient do reactor.netty para configurar os tempos de conexão e de resposta. Isso garante que a API cliente não fique bloqueada por chamadas lentas da API NeoWS.
Filtro para Adicionar a api_key: A API NeoWS exige que a chave da API seja passada como parâmetro de consulta. Para isso, criamos um filtro (ExchangeFilterFunction) que intercepta as requisições e adiciona a chave da API automaticamente.
WebClient e Conexão Reativa: O WebClient.builder() é utilizado para configurar o cliente HTTP. Ele define a URL base, os headers padrão e inclui o filtro da chave da API.
Com esta configuração, temos um cliente reativo pronto para interagir com a API NeoWS, com suporte a timeouts, autenticação e tratamento de grandes volumes de dados.
Serviço da Aplicação
O serviço é a camada intermediária entre o controlador e a API externa. Ele é responsável por fazer as chamadas HTTP utilizando o WebClient configurado e retornar as respostas de forma reativa. Pense nela como o cérebro lógico por trás de uma funcionalidade específica do seu sistema. Ela encapsula a lógica de negócios, as regras e os processos necessários para realizar uma determinada tarefa.
@Service
public class NeoWsService {
@Autowired
private WebClient webClient;
private static final String DATE_FORMAT = "yyyy-MM-dd";
//Método para NEO FEED
public Mono<NeoFeedResponse> getNeoFeed(LocalDate startDate, LocalDate endDate){
String formattedStartDate = startDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
String formattedEndDate = endDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
return webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/feed")
.queryParam("start_date", formattedStartDate)
.queryParam("end_date", formattedEndDate)
.build())
.retrieve()
.bodyToMono(NeoFeedResponse.class);
}
//Método para NEO - LOOKUP
public Mono<Asteroid> getNeoLookup(String asteroidId){
return webClient.get()
.uri("/neo/" + asteroidId)
.retrieve()
.bodyToMono(Asteroid.class);
}
//Método para NEO - BROWSE
public Mono<NeoBrowseResponse> browseNeo(){
return webClient.get()
.uri("/neo/browse")
.retrieve()
.bodyToMono(NeoBrowseResponse.class);
}
}
Explicação dos Métodos
getNeoFeed: Recebe duas datas como parâmetros, formata-as e as passa como parâmetros para a requisição GET na API NeoWS. Retorna um Mono<NeoFeedResponse>, que é uma resposta assíncrona com a lista de asteroides.
getNeoLookup: Recebe o ID de um asteroide e faz uma requisição GET para buscar as informações detalhadas daquele asteroide específico. Retorna um Mono<Asteroid>.
browseNeo: Faz uma requisição GET para navegar pelos dados gerais dos asteroides, retornando um Mono<NeoBrowseResponse>.
Controlador da Aplicação
O controlador da aplicação é responsável por mapear as requisições HTTP e delegar o processamento para o serviço correspondente.Se a classe de serviço é o cérebro lógico, o Controller é como o porteiro ou o maestro da API. Ele fica na fronteira, recebendo as "ordens" do mundo exterior (requisições HTTP) e as direcionando para o "cérebro" (a classe de serviço) para que o trabalho real seja feito. Depois, ele pega o "resultado" do serviço e o formata para enviar de volta como uma "resposta" para quem fez a pergunta. O código abaixo apresenta o controlador da nossa API cliente:
@RestController
@RequestMapping("/api/neo")
public class NeoWsController {
@Autowired
private final NeoWsService neoWsService;
@GetMapping("/feed")
public Mono<NeoFeedResponse> getNeoFeed(
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate){
return neoWsService.getNeoFeed(startDate, endDate);
}
@GetMapping("/lookup/{asteroidId}")
public Mono<Asteroid> getNeoLookup(@PathVariable String asteroidId){
return neoWsService.getNeoLookup(asteroidId);
}
@GetMapping("/browse")
public Mono<NeoBrowseResponse> browseNeo(){
return neoWsService.browseNeo();
}
}
Explicação dos Métodos
Neo Feed (GET /feed): Este endpoint recupera uma lista de asteroides, filtrada por um intervalo de datas (startDate e endDate). O serviço chama o método getNeoFeed para buscar os dados da API NeoWS.
Neo Lookup (GET /lookup/{asteroidId}): O método busca informações detalhadas sobre um asteroide específico utilizando o seu ID da NASA (SPK-ID). O serviço chama o método getNeoLookup para buscar os dados relacionados a esse asteroide.
Neo Browse (GET /browse): Este endpoint permite navegar pelos dados gerais de asteroides com suporte à paginação. Ele chama o método browseNeo do serviço para retornar os dados.
Resultado da Busca
A fim de validar a funcionalidade da nossa API, selecionamos o asteroide 2024 YR4 (identificado pelo SPKID: 54509621). Este objeto astronômico, que recentemente atraiu considerável atenção da mídia devido a especulações sobre um possível impacto com a Terra, será utilizado para realizar uma requisição ao endpoint /lookup/{asteroidId}. A ferramenta Insomnia será empregada para construir e enviar a solicitação, com o SPKID sendo fornecido como parâmetro na URL.
A resposta à nossa requisição será um arquivo JSON completo com os dados do asteroide. Especificamente, os detalhes da sua maior aproximação com a Terra podem ser encontrados no array "close_approach_data". Dentro desse array, o objeto com o menor valor no campo "miss_distance" (em quilômetros ou unidades astronômicas) indica o momento de máxima proximidade.
Em resumo, essa aproximação representa um evento astronômico interessante que permitirá um estudo científico mais aprofundado do asteroide 2024 YR4, sem representar uma ameaça imediata de colisão com o nosso planeta, de acordo com os dados fornecidos. O monitoramento contínuo é uma prática padrão para objetos próximos da Terra.
Conclusão
Neste artigo, apresentamos uma implementação básica de uma API cliente utilizando o Spring WebFlux e o WebClient para interagir com a API NeoWS da NASA. A escolha do Spring WebFlux permite que a nossa aplicação lide de forma eficiente com chamadas assíncronas e não bloqueantes, proporcionando uma melhor escalabilidade e desempenho, especialmente ao interagir com grandes volumes de dados. A configuraçãodo WebClient, que inclui a adição de timeouts e o filtro para adicionar a chave da API, é um aspecto crucial para garantir a robustez e a segurança da aplicação.
Agradeço sinceramente a você por dedicar seu tempo à leitura desta implementação básica. Espero que tenha sido útil para compreender os conceitos apresentados. Como este é apenas um ponto de partida, qualquer sugestão, crítica construtiva ou ideia para aprimorar esta implementação é extremamente bem-vinda. Seu feedback é valioso para o meu aprendizado e para a evolução deste trabalho. Obrigado novamente!