Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Clima API - Belo Horizonte

API REST em Java com Spring Boot que consome uma API externa de meteorologia e disponibiliza as informações climáticas de Belo Horizonte - MG em JSON.

Atividade 01 - API REST de Clima com Spring Boot.

API externa utilizada

Open-Meteo. Escolhemos ela porque é gratuita e não exige API Key nem cadastro, então nenhuma credencial precisa ser publicada no repositório.

Tecnologias

  • Java 21
  • Spring Boot 4.1.1
  • Maven (via wrapper, não precisa instalar)

Dependências do pom.xml:

Dependência Uso
spring-boot-starter-webmvc Spring MVC, Tomcat embarcado e conversão JSON
spring-boot-starter-validation validação dos parâmetros dos endpoints
spring-boot-configuration-processor metadados das propriedades clima.*
spring-boot-starter-webmvc-test JUnit 5, Mockito, AssertJ e MockMvc (testes)
spring-boot-starter-validation-test validação nos testes

A chamada HTTP para a API externa usa o RestClient do Spring, que já vem no spring-web.

Como executar

Pré-requisitos: JDK 21 ou superior e conexão com a internet.

git clone https://github.com/<usuario>/<repositorio>.git
cd <repositorio>

Windows:

.\mvnw.cmd spring-boot:run

Linux / macOS:

./mvnw spring-boot:run

Depois que aparecer Started ClimaApiApplication, acesse:

http://localhost:8080/clima

Para gerar e rodar o jar:

./mvnw clean package
java -jar target/clima-api-0.0.1-SNAPSHOT.jar

Para trocar a porta:

./mvnw spring-boot:run "-Dspring-boot.run.arguments=--server.port=8081"

Configuração da API Key

A Open-Meteo não exige API Key, então a aplicação roda sem configuração adicional.

Mesmo assim a chave não fica no código. Em application.properties:

clima.api-key=${CLIMA_API_KEY:}

Se a API externa for trocada por uma que exija autenticação (WeatherAPI, OpenWeather, Tomorrow.io), basta definir a variável de ambiente:

# Windows
$env:CLIMA_API_KEY = "sua-chave-aqui"
# Linux / macOS
export CLIMA_API_KEY="sua-chave-aqui"

Ou por linha de comando:

./mvnw spring-boot:run "-Dspring-boot.run.arguments=--clima.api-key=sua-chave-aqui"

Quando a propriedade está preenchida, o OpenMeteoClient envia o parâmetro apikey na requisição. Quando está vazia, o parâmetro é omitido.

O .gitignore bloqueia .env, *.env, application-local.properties e application-secret.properties.

Outras propriedades

Propriedade Padrão
clima.api-url https://api.open-meteo.com/v1/forecast
clima.geocoding-url https://geocoding-api.open-meteo.com/v1/search
clima.api-key vazio (lido de CLIMA_API_KEY)
clima.timezone America/Sao_Paulo
clima.connect-timeout 5s
clima.read-timeout 10s
clima.cidade-padrao.* Belo Horizonte / MG / -19.9167, -43.9345

Endpoints

Base: http://localhost:8080

Método Endpoint Descrição
GET /clima clima atual de Belo Horizonte
GET /clima/belo-horizonte mesmo resultado do anterior
GET /clima/previsao?dias={n} previsão dos próximos dias em BH
GET /clima/cidade/{cidade} clima atual de outra cidade
GET /clima/cidade/{cidade}/previsao?dias={n} previsão de outra cidade

O parâmetro dias é opcional, vale 7 por padrão e aceita valores de 1 a 16.

GET /clima

curl http://localhost:8080/clima

Resposta (200):

{
  "localizacao": {
    "cidade": "Belo Horizonte",
    "estado": "Minas Gerais",
    "pais": "Brasil",
    "latitude": -19.9297,
    "longitude": -43.966034,
    "altitude": 843.0,
    "fusoHorario": "America/Sao_Paulo"
  },
  "consultadoEm": "2026-08-24T19:44:32.117-03:00",
  "observadoEm": "2026-08-24T19:30",
  "condicao": "Garoa moderada",
  "descricao": "Garoa moderada em Belo Horizonte, com 19,1 C e umidade de 81%.",
  "codigoTempo": 53,
  "temperatura": {
    "atual": 19.1,
    "sensacaoTermica": 19.7,
    "maxima": 26.1,
    "minima": 16.4,
    "unidade": "C"
  },
  "umidade": 81,
  "vento": {
    "velocidade": 9.7,
    "unidade": "km/h",
    "direcaoGraus": 132,
    "direcaoCardeal": "SE"
  },
  "precipitacao": 0.2,
  "pressao": 1019.6,
  "periodo": "noite",
  "fonte": "Open-Meteo (https://open-meteo.com)"
}

GET /clima/previsao

curl "http://localhost:8080/clima/previsao?dias=3"

Resposta (200):

{
  "localizacao": {
    "cidade": "Belo Horizonte",
    "estado": "Minas Gerais",
    "pais": "Brasil",
    "latitude": -19.9297,
    "longitude": -43.966034,
    "altitude": 843.0,
    "fusoHorario": "America/Sao_Paulo"
  },
  "consultadoEm": "2026-08-24T19:44:32.728-03:00",
  "quantidadeDias": 3,
  "previsoes": [
    {
      "data": "2026-08-24",
      "diaSemana": "segunda-feira",
      "condicao": "Garoa moderada",
      "codigoTempo": 53,
      "temperaturaMaxima": 26.1,
      "temperaturaMinima": 16.4,
      "probabilidadeChuva": 75,
      "precipitacaoTotal": 1.5,
      "ventoMaximo": 15.6,
      "nascerDoSol": "2026-08-24T06:10",
      "porDoSol": "2026-08-24T17:45"
    },
    {
      "data": "2026-08-25",
      "diaSemana": "terça-feira",
      "condicao": "Nublado",
      "codigoTempo": 3,
      "temperaturaMaxima": 27.4,
      "temperaturaMinima": 16.4,
      "probabilidadeChuva": 47,
      "precipitacaoTotal": 0.0,
      "ventoMaximo": 11.6,
      "nascerDoSol": "2026-08-25T06:09",
      "porDoSol": "2026-08-25T17:45"
    }
  ],
  "fonte": "Open-Meteo (https://open-meteo.com)"
}

GET /clima/cidade/{cidade}

O nome da cidade é convertido em coordenadas pelo serviço de geocoding da Open-Meteo. Acentos e espaços precisam ser codificados na URL.

curl "http://localhost:8080/clima/cidade/Curitiba"
curl "http://localhost:8080/clima/cidade/S%C3%A3o%20Paulo"

O formato da resposta é o mesmo de GET /clima.

GET /clima/cidade/{cidade}/previsao

curl "http://localhost:8080/clima/cidade/Curitiba/previsao?dias=5"

Mesmo formato de GET /clima/previsao.

Tratamento de erros

Os erros passam pelo GlobalExceptionHandler e saem sempre no mesmo formato:

{
  "timestamp": "2026-08-24T19:44:36.463-03:00",
  "status": 404,
  "erro": "Cidade nao encontrada",
  "mensagem": "Nao foi possivel localizar a cidade informada: Atlantida123",
  "caminho": "/clima/cidade/Atlantida123"
}
Status Quando acontece
400 dias fora do intervalo 1-16 ou não numérico
404 cidade não encontrada no geocoding
404 rota inexistente
502 a API externa respondeu com erro ou com corpo ilegível
502 a API externa respondeu sem os dados meteorológicos
503 timeout, sem rede ou conexão recusada com a API externa
500 qualquer falha não prevista

Os timeouts (clima.connect-timeout e clima.read-timeout) evitam que uma queda da API externa deixe as requisições travadas.

Testes

./mvnw test

Os testes usam mocks no lugar do cliente HTTP, então rodam sem acessar a internet.

Classe O que cobre
ClimaServiceTest conversão dos dados da API externa e cenários de dados ausentes
ClimaControllerTest endpoints, formato do JSON e status de erro
CodigoTempoTest tradução dos códigos de condição climática
DirecaoVentoTest conversão de graus para ponto cardeal
ClimaApiApplicationTests carregamento do contexto

Estrutura

src/main/java/com/faculdade/clima/
├── ClimaApiApplication.java
├── config/          ClimaProperties, RestClientConfig
├── controller/      ClimaController, GlobalExceptionHandler
├── service/         ClimaService
├── client/          OpenMeteoClient e os DTOs da API externa
├── dto/             objetos devolvidos pela nossa API
├── exception/       exceções próprias
└── util/            CodigoTempo, DirecaoVento

Os DTOs ficam separados em dois pacotes: client/dto tem o formato da Open-Meteo e dto tem o formato que a nossa API devolve. Assim uma mudança na API externa não muda o contrato dos nossos endpoints.

Funcionalidades extras

  • consulta do clima de outras cidades pelo nome
  • previsão para os próximos dias (1 a 16)
  • dados organizados em objetos próprios da aplicação
  • tratamento de erro separado por tipo de falha da API externa
  • tradução dos códigos de condição climática para português
  • direção do vento convertida para ponto cardeal

Autores

Integrante GitHub

About

Alunos: Mario e Rafael

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages