Pratique com APIs de verdade - ClimaTempo
Tags: api, beginners, go, rest
Published: 2023-08-10 01:25:18

Já aconteceu mais de uma vez de pessoas que estão começando me pedirem dicas de projetos para começar. Ainda nessa questão de pedir dicas, também já me perguntaram como poderia praticar consumir APIs em projetos backend para terem a sensação que estão construindo algo que faça sentido e não algo que eles mesmos inventaram. 

Um tempo atrás, eu fui apresentado ao [TMDB](https://www.themoviedb.org/about) (The Movie DB), que é basicamente um serviço online criado e mantido pela comunidade e que você consegue consumir informações referentes a filmes e series. Esse serviço já é excepcional pro nosso propósito e, francamente, já usei algumas vezes pra praticar com novos frameworks e ter um resultado mais agradável e até mesmo mais real. Só que... ainda é uma coisa meio como se eu mesmo tivesse feito. Não tem aquela sensação boa de aplicação real e prática. 

Foi quando, navegando pela internet esses dias, eu descobri que o ClimaTempo tem uma API que te dá resultados reais sobre clima e tempo (sem trocadilhos com o nome). E você encontra essa API no endereço [https://advisor.climatempo.com.br/](https://advisor.climatempo.com.br/) 

## A API ClimaTempo

### A API em números

Eles oferecem um serviço que dispõe de uma versão free (obviamente com algumas limitações) e prometem ter um uptime de 99,8%. O que considero um valor bem satisfatório. 

Além disso, também fornecem uma documentação bem interessante e bem explicada da sua API que será fornecida usando o modelo REST. 

Também oferecem um dashboard bem interessante que te ajuda a acompanhar o quanto você está dentro do seu plano. 

### O Dashboard 

Quando você cria sua conta, uma das primeiras coisas que você vai ter acesso é ao Board. Que vai te ajudar a ver informações sobre o status da API, o uptime, o tempo médio de resposta e quantos hits/dia você já vez. 

Lembrando que o plano free te oferece um limite de 10req/min ou 100req/dia. Mais do que suficiente. 

### A Documentação 

A documentação não foge muito do padrão e você pode encontrar [aqui](http://apiadvisor.climatempo.com.br/doc/index.html). Mas como a forma com que modelamos uma API reflete muito na doc, aqui vale ressaltar que considero o contrato que foi desenhado nessa API bem interessante e que pode servir de inspiração caso você esteja criando uma do zero e esteja com algumas dúvidas sobre como estruturar. 

Aqui pegamos o gancho para seu token de acesso. Todos (os que eu vi, ao menos) endpoints vão pedir para você passar seu token como query parameter. 

Aqui uma opinião pessoal: acho estranho passar o token dessa forma. Eu optaria por passar via header, mas tudo bem. 

E esse token você consegue gerar um novo com muita facilidade no painel. 

Também é outro ponto que eu talvez mudasse. Esse modelo de token é um pouco inseguro, pois em caso de vazamento, ele não é gerado novamente depois de x minutos. O que pode ser um problema, mas como aqui não estamos falando de dados sensíveis, também não é o fim do mundo. Você só teria indisponibilidade por atingir o máximo de acessos diários. 

## Colocando em prática

Pra esse post não ficar apenas na pegada de textão, vamos fazer uma integração simples usando Go da forma mais básica possível?

Vamos começar criando um projeto e para isso, vou usar toda a minha criatividade de dev para nomear coisas e criar uma pasta chamada `climatempo-go` 

```
$ mkdir climatempo-go
$ go mod init github.com/marcopollivier/climatempo-go
```

Para esse projeto eu estou usando ainda a versão 1.20 (a 21 saiu ontem... dá o desconto) 

E aqui pra esse primeiro post, eu vou simplesmente criar um `main.go` e fazer tudo dentro dele. Eu coloquei ali nas tags que era pra iniciante, então não reclama. Vou tentar fazer outros posts avançando com esse projeto. 

Vamos de Hello, world pra ver se tá tudo bem com meu ambiente. Eu SEMPRE faço isso. 

```go
package main

import "fmt"

func main() {
	fmt.Println("Oi, mundo")
}
```

Como eu vou usar o pacote `net/http` vamos de [documentação](https://pkg.go.dev/net/http) ou de [go by example](https://gobyexample.com/http-client) pra ter certeza que nada mudou. 

Ah... aqui, como nós vamos pegar uma informação de algum lugar, nós vamos ser considerados clientes, se pensarmos em uma estrutura cliente/servidor. 

> Se você tiver dúvidas em relação a isso, pense: eu estou pedindo ou servindo... acho que agora ficou claro, certo? 

Bom. Vamos de condições atuais do tempo. Você vai encontrar essa parte aqui nesse endereço da documentação: [http://apiadvisor.climatempo.com.br/doc/index.html#api-Weather-CurrentWeatherByCity](http://apiadvisor.climatempo.com.br/doc/index.html#api-Weather-CurrentWeatherByCity)

E de acordo com ela, faremos um `GET` no endpoint `/api/v1/weather/locale/:id/current?token=your-app-token
`

O que, no fim, ficaria algo parecido com isso aqui, caso chamássemos via terminal. 

```shell
$ curl -i http://apiadvisor.climatempo.com.br/api/v1/weather/locale/3477/current?token=your-app-token
```

Traduzindo isso para código Go, teríamos: 

```go
package main

import "net/http"

func main() {
	http.Get("http://apiadvisor.climatempo.com.br/api/v1/weather/locale/3477/current?token=your-app-token")
}

```

Mas isso ainda não faz nada... quer dizer... esse Get até acontece, mas não pegamos essa informação e criamos alguma coisa com ela. 

Aqui vale um passo atrás e mostrar que não tirei nada da minha cabeça. Eu sei que você já clicou e viu a documentação que mandei ali em cima, mas caso o texto tenha envolvido tanto você que fez você perder essa oportunidade, você vai perceber algumas coisas na documentação. 

Ela vai ser composta por:
1. o contrato (endereço e método HTTP) 
2. um exemplo de como fica esse contrato (usando o curl por padrão) 
3. os parâmetros que você precisa passar. 
4. O que essa API vai retornar quando tiver em um cenário de sucesso
5. E o que ela vai retornar em um cenário de erro.

E o nosso código vai precisar representar cada uma dessas informações. Vamos ver isso acontecer? 

### O contrato
Vamos começar por essa parte do nosso contrato. Onde tempos os 3 primeiros items dessa lista acima. 

![Contrato da api do Climatempo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5sk1gtf5lgncbzh2xu02.png)

E vamos fazer essa correlação com o código que estamos criando. 

![Correlação entre código e documentação da API do ClimaTempo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5ubgc8bid4lpptkw3n4.png)


![E aqui os parametros](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/feg5vvr203kuu8xr5kis.png)


Dessa forma, vamos evoluir nosso código da seguinte forma. 

```go 
package main

import (
	"bufio"
	"fmt"
	"net/http"
)

func main() {
	resp, _ := http.Get("http://apiadvisor.climatempo.com.br/api/v1/weather/locale/3477/current?token=your-app-token")
	defer resp.Body.Close()

	fmt.Println(resp.StatusCode)

	scanner := bufio.NewScanner(resp.Body)
	for i := 0; scanner.Scan() && i < 5; i++ {
		fmt.Println(scanner.Text())
	}

}
```

Dando uma resumida no que esse código está fazendo:

- ele pega a resposta desse Get e atribui o valor pra variável `resp`. Como essa função http.Get tem um retorno multivalorado, o segundo parâmetro seria um erro, mas pra esse exemplo não quero me preocupar com isso. De modo que uso `_`(underline) para dizer que não quero fazer nada com esse retorno. 

Um disclaimer sobre o underline: esse aqui é um exemplo meramente didático pra falarmos sobre o processo básico de consumir uma API. Evite ao máximo usar isso em um código que vá para o ambiente produtivo, especialmente se for um retorno de erro como era esse caso. 

Logo depois de pegar o retorno, eu chamo um `defer` que vai chamar a função de `Body.Close()` e colocar no final da pilha de execuções. Dessa forma depois de executar tudo que está previsto nessa função, ele vai fechar essa conexão da chamada. 

Depois disso, imprimimos o status code dessa chamada. Se voltarmos a documentação, sabemos que isso está devidamente documentado lá. No caso, quando temos o exemplo de sucesso, ela afirma que retornará um código `200` e quando tiver um erro, retornará algo com 4xx. 

Esse 4xx representa que retornará algum código da família 400. (400: bad request, 401: Unauthorized etc). Veja mais [nesse artigo](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)

![Exemplo de erro como está documentado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/werp2mk94qrq3lfstilq.png)

Nesse momento se executarmos o código exatamente como está, ele irá retornar um desses erros. No caso retornará um erro 400 com o seguinte JSON de erro

```json
{"error":true,"detail":"Invalid token"}
```

Isso está acontecendo justamente por não termos mudado o token no endereço ali em cima. 

```
http://apiadvisor.climatempo.com.br/api/v1/weather/locale/3477/current?token=your-app-token
```

Mude o texto onde está `your-app-token` para o token que aparecer no seu painel. CUIDADO para não deixar isso público e nem compartilhar isso com ninguém. 

Com isso passaremos a ter uma resposta com o padrão de sucesso como apresentado na documentação. Nesse caso, teremos um retorno `200` e um JSON parecido com esse: 

```json 
{
   "id":3477,
   "name":"S\u00e3o Paulo",
   "state":"SP",
   "country":"BR  ",
   "data":{
      "temperature":17,
      "wind_direction":"SSE",
      "wind_velocity":19,
      "humidity":94,
      "condition":"Chuva fraca",
      "pressure":1023,
      "icon":"5n",
      "sensation":17,
      "date":"2023-08-09 19:20:08"
   }
}
```

## Conclusão

Eu vou parar por aqui nesse exemplo para não ficar cansativo. Principalmente se pensarmos que esse primeiro post sobre esse tema foi bem direcionado para apresentar a API do ClimaTempo e a parte de código bem direcionada para um público iniciante, acredito que onde paramos é um bom momento. 

Nesse post conseguimos ver detalhes de como entender uma documentação de uma API. Vimos que o ClimaTempo pode ser um bom projeto pra praticarmos alguma tecnologia nova na hora de testar soluções de client http. 

E também oficializamos esse exemplo com um código básico usando o pacote `net/http` do Go. 

### Próximos passos

Provavelmente o próximo passo vai ser a evolução desse aqui. vamos melhorar um pouco nosso projeto transformando nosso JSON em uma struct por exemplo. 

Se você gostou desse post, curta e compartilhe nas redes. Qualquer duvida ou feedback deixe nos comentários e [me siga nas redes](https://linktr.ee/marcopollivier) :) 

Até mais.