Criando uma API Rest com Fiber - Uma história pessoal de aprendizado
Tags: webdev, go, beginners, programming
Published: 2022-11-22 18:20:39

Todos temos jornadas diferentes quando nos propomos a aprender algo novo, e eu tive um pouco de dificuldade quando comecei a aprender Go.

Passei bons anos trabalhando com Node.js, e seu ecossistema me deixou mal acostumado com a facilidade e disponibilidade de bibliotecas de terceiros.

![Meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezoq3o62qenjukk4xrvq.png)

Depois de ler a documentação e estudar alguns snippets a fim de entender como a linguagem funcionava, meu cérebro automaticamente me fez abrir o Google e pesquisar "best go frameworks", e eu fui (felizmente) bombardeado com várias opções. Tentei me aventurar com os frameworks MVC, com a ideia de acelerar o desenvolvimento com ferramentas que já "fizessem tudo por mim"... Sem sucesso.

Comecei a esbarrar em conceitos básicos que não havia entendido muito bem, e estruturas de pastas que não significavam nada pra mim, então precisei dar alguns passos para trás, pois estava há um tempo considerável na zona de conforto de uma linguagem que já conhecia.

_Antes de continuar, precisamos esclarecer algumas coisas:_

1. O artigo diz única e exclusivamente sobre a minha jornada pessoal com Go, e não tem intenção nenhuma de ser científico ou a palavra final sobre o assunto.
2. Se quiser ir direto ao ponto onde eu mostro qual meu roadmap de estudos, pode pular pro final do artigo.
3. Go não é orientado a objetos, e nem se propõe a garantir um suporte completo a hierarquia ou classes (temos formas de mimetizar esse comportamento, mas em casos específicos).
4. Sim, você vai precisar lidar com todos os erros que ocorrerem.
5. Go é uma linguagem focada em performance, ela se propõe a atingir o desempenho de linguagens como C, segundo a própria [doc](https://go.dev/doc/faq#Why_does_Go_perform_badly_on_benchmark_x): "One of Go's design goals is to approach the performance of C for comparable programs [...]".
6. O mascote é super fofo.

![Mascote Gopher](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2458iqqpqvws0z4zcui.png)

Voltando ao assunto, a zona de conforto me fez esperar (por algum motivo) que Go funcionasse da mesma forma, e que eu poderia achar tudo que eu precisava dentro de um gerenciador de pacotes. Em resumo: eu estava errado, e nesse meio tempo um colega de trabalho (que acabou se tornando um amigo) que já usava Go há bastante tempo entrou na minha equipe. Ele me esclareceu muita coisa e foi o responsável por acelerar meu aprendizado, e o que mudou meu ponto de vista foi entender que a linguagem foca na simplicidade ao invés de possuir várias maneiras de fazer a mesma coisa, um exemplo disso são loops: Go só possui o loop `for`:

For?

```go
sum := 0
for i := 1; i < 5; i++ {
	sum += i
}
fmt.Println(sum) // 10 (1+2+3+4)
```

For-each?

```go
strings := []string{"hello", "world"}
for i, s := range strings {
	fmt.Println(i, s)
}
```

While?

```go
n := 1
for n < 5 {
	n *= 2
}
fmt.Println(n) // 8 (1*2*2*2)
```

Loop infinito?

```go
sum := 0
for {
	sum++ // repete para sempre
}
fmt.Println(sum) // nunca é alcançado
```

Esse conceito se estende também às convenções da linguagem como:

**Formatação** - O pacote `gofmt` é responsável por toda a formatação do código. Ele é o padrão utilizado, e em alguns casos um erro de indentação pode até impedir seu programa de ser compilado.

Um exemplo de formatação errada:

```go
type T struct {
    name string // name of the object
    value int // its value
}
```

O mesmo trecho após rodar o `gofmt`:

```go
type T struct {
    name    string // name of the object
    value   int    // its value
}
```

**Nomes de pacotes** - Quando um pacote é importado em Go, o nome que foi importado é o que vai ser utilizado no código para acessar suas funções:

```go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	fmt.Println(http.StatusOK)
}
```

Isso significa que os nomes dos pacotes devem ser curtos, concisos e evocativos. Por convenção, os nomes de pacotes devem ser lower-case e uma única palavra, sem a necessidade de underlines ou traços. O que isso quer dizer? Quer dizer que os nomes devem ser memoráveis e auto explicativos o suficiente, para serem reduzidos a uma palavra.

Exemplos: `net/http`, `bytes`, `log`, `user`, `routes`, etc.

Em alguns casos pode ser difícil reduzir um nome complexo a uma só palavra, nesses casos podemos abrir exceção, mas sempre usando do bom senso.
E como fica a questão de colisão de nomes? Bom, o nome do pacote pode ser alterado no momento do `import`, dessa forma poderiamos chamar o pacote `net/http` de `httpzinho`:

```go
package main

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

func main() {
	fmt.Println(httpzinho.StatusOK)
}
```

**Nomes de funções** - Os nomes de funções em Go possuem uma peculiaridade: se começarem com a letra maiúscula a função é exportada pelo pacote, caso o nome comece com uma letra minúscula, ele não é exportado:

a.go

```go
package a

func Exported() string {
	return "Hi, I'm exported"
}

func unexported() string {
	return "Hi, I'm unexported"
}
```

main.go

```go
package main

import "a"

func main() {
	a.Exported()
	a.unexported() // vai retornar um erro
}
```

Esses são só alguns pontos de convenções da linguagem, o restante pode ser encontrado num [guia oficial da linguagem](https://go.dev/doc/effective_go).

Além de simples Go também é "batteries included", que traduzindo livremente seria: "pilhas inclusas". Esse termo faz referência aos brinquedos que tinham um aviso na caixa, sinalizando que não vinham com pilhas (batteries not included) e elas precisavam ser compradas separadamente.
Adaptando para a programação, linguagens com "pilhas inclusas" proporcionam um leque de ferramentas e bibliotecas nativas para resolver problemas comuns, como servidores HTTP, testes, etc.
Go possui essas bibliotecas e podem ser encontradas [aqui](https://pkg.go.dev/std)

_**Porém algumas "pilhas" não estão inclusas:**_
**Logging** - Assim como muitas linguagens, Go tem funções para mostrar informações no console e até um [pacote](https://golang.org/pkg/log/), mas as soluções são muito simples e insuficientes. Uma alternativa que gosto muito de utilizar é o [logrus](https://github.com/sirupsen/logrus).

```go
package main

import (
	log "github.com/sirupsen/logrus"
)

func main() {
	log.Info("Hello World! Info")
	log.Warn("Hello World! Warn")
	log.Fatal("Hello World! Fatal")
}
```

**Test assertions** - O pacote nativo do Go para testes é muito bom e poderia render um artigo inteiro só sobre ele (mas um resumo pode ser encontrado no [guia oficial](https://go.dev/doc/tutorial/add-a-test)). O pacote atendeu todos os casos que precisei, mas uma coisa que começa a fazer falta depois de um tempo são funções de comparação ou "assertions".

Normalmente, você faria algo do tipo:

```go
package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

// Hello returns a greeting for the named person.
func Hello(name string) string {
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
	msg := Hello("")
	if msg != "" {
		t.Fatalf(`Hello("") = %q, want "", error`, msg)
	}
}
```

OUTPUT

```
=== RUN   TestHelloEmpty
    prog.go:20: Hello("") = "Hi, . Welcome!", want "", error
--- FAIL: TestHelloEmpty (0.00s)
FAIL
```

Nesse caso, estamos usando o pacote `testing`, porém quando checamos nosso caso de erro, fazemos um if explícito `if msg != "" {`, e então retornamos um erro com a mensagem informando a diferença entre o resultado atual e o esperado, depois de algum tempo essa estrutura fica gigantesca, e em casos de teste muito específicos acaba dificultando sua manutenção.
A alternativa nesse caso, é utilizar um pacote de [assertion](https://github.com/stretchr/testify/), e nossa sintaxe muda pra isso:

```go
package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

// Hello returns a greeting for the named person.
func Hello(name string) string {
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
	msg := Hello("")
	assert.Equalf(t, "", msg, "Empty string Hello")
}
```

OUTPUT

```
=== RUN   TestHelloEmpty
    prog.go:21:
        	Error Trace:	/prog.go:21
        	Error:      	Not equal:
        	            	expected: ""
        	            	actual  : "Hi, . Welcome!"

        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-
        	            	+Hi, . Welcome!
        	Test:       	TestHelloEmpty
        	Messages:   	Empty string Hello
--- FAIL: TestHelloEmpty (0.00s)
FAIL
```

Muito mais simples né!? E além de tudo, ainda temos um retorno muito mais explícito e explicativo no console, agilizando a correção do erro.

Outras "pilhas" estão inclusas, mas podem ser trocadas por outras mais potentes, como é o caso do pacote `net/http`. Um exemplo de um servidor HTTP com pacotes nativos:

```go
package main

import (
	"fmt"
	"net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "hello\n")
}

func main() {
	http.HandleFunc("/hello", hello)
	http.ListenAndServe(":8090", nil)
}
```

Bem direto ao ponto. Mas e se quisermos adicionar uma leitura dos headers da requisição?

```go
package main

import (
	"fmt"
	"net/http"

	"github.com/sirupsen/logrus"
)

func hello(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {
	for name, headers := range req.Header {
		for _, h := range headers {
			fmt.Fprintf(w, "%v: %v\n", name, h)
		}
	}
}

func main() {

	http.HandleFunc("/hello", hello)
	http.HandleFunc("/headers", headers)

	http.ListenAndServe(":8090", nil)
}
```

Agora ficou um pouco mais complicado, e isso tem tendência a escalar cada vez mais quando começamos a adicionar tempo de processamento nas nossas requisições, manipulando `channels` e o `Context` da requisição.
Em casos como esses, é prudente utilizar pacotes que abstraem essa complexidade, e um desses pacotes vai ser o tema da continuação dessa série de artigos.

## Conclusão
Levando tudo isso em consideração, comecei a estudar diretamente as bibliotecas nativas da linguagem, e tentando fazer tudo o mais simples possível. A recompensa de optar pelo simples em Go é muito grande por alguns motivos:

1. Reforça as **particularidades** e **convenções** da linguagem.
2. Quanto **menos** dependências forem usadas, **melhor** — Go é uma linguagem compilada, e binários pequenos são sempre bem-vindos.
3. Os pacotes utilizados tendem a ser importados para resolver problemas **necessários**, e não para qualquer fim, tornando as dependências fáceis de serem gerenciadas.
4. Capacidade de debugar bibliotecas de terceiros. Os pacotes open-source tem a tendência de seguir a **convenção oficial** e o padrão de código dos pacotes nativos, uma vez que esses padrões estão memorizados, fica fácil entender o que códigos de pacotes complexos estão fazendo no seu código.

A forma que consegui atingir esse conhecimento foi inicialmente fazendo um curso na Udemy. Nesse curso o professor Otávio Augusto Gallego faz um tour pelas estruturas mais básicas da linguagem, escalando gradativamente até chegar na construção de uma rede social 100% em Go com o padrão MVC.

Enquanto seguia o curso, eu criei um repositório próprio, documentando e organizando cada conceito da linguagem em pastas, que pode ser encontrado [aqui](https://github.com/un-versed/learning-go). A estrutura do repositório é parecida com a criada pelo professor no curso.

Depois disso fiz alguns projetos de exemplo, como [esse](https://github.com/un-versed/base-api-go), que é um CRUD básico em Go, utilizando 3 abordagens diferentes de acesso ao banco de dados. E também utilizei [esse projeto para estudos](https://github.com/un-versed/whatsapp-cats), cujo objetivo é automatizar o envio de fotos de gatinhos fofos pelo Whatsapp.
Além desses pontos, a leitura dos materiais oficiais da linguagem é imprescindível.

Um resumo do roadmap de estudos:

- Curso da Udemy 100% em português
  - [Aprenda Golang do Zero! Desenvolva uma APLICAÇÃO COMPLETA!](https://www.udemy.com/course/aprenda-golang-do-zero-desenvolva-uma-aplicacao-completa/)
- Links oficiais pra aprender Go gratuitamente:
  - [A Tour of Go](https://go.dev/tour/welcome/1)
  - [How to Write Go Code](https://go.dev/doc/code)
  - [Effective Go](https://go.dev/doc/effective_go)
  - [Go by Example](https://gobyexample.com/)
- Criar um repositório de estudos
- Criar projetos open-source como treino.
- Perguntar e trocar código com outras pessoas que já saibam ou estejam aprendendo Go.

Nos próximos artigos vou mostrar como fazer uma API usando o [Fiber](https://github.com/gofiber/fiber)!

Se tiver algum código de estudo pra compartilhar, só mandar na dm!