Implementando uma API com gorilla/mux

Sem dúvidas um dos routers mais famosos e utilizados no mundo Golang, hoje vamos ver como implementar uma API utilizando gorilla/mux.

Para quem não conhecer, gorilla/mux ajuda na hora de fazer o match da URL que está sendo chamada com a função que vai tratar aquela URL. Além dessa facilidade, um dos principais benefícios de se utilizar gorilla/mux é que ele implementa a interface http.Handler nativa do Go.

Sem mais delongas, vamos começar implementar nossa API instalando o gorilla/mux.

$ go get -u github.com/gorilla/mux

Agora que já temos nossa dependência instalada no sistema, vamos começar nossa API criando um arquivo main.go e importando as dependências que vamos precisar.

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

Feito isso, vamos escrever uma pequena função para retornar um Hello world!.

func HandleHello(w http.ResponseWriter, rq *http.Request) {
    fmt.Fprintf(w, "Hello world!\n")
}

No caso dessa função, nós não vamos usar nada da request, mas mesmo assim precisamos coloca-la na assinatura da função.

Agora vamos levantar nossa API criando a principal função do Go.

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HandleHello)

    http.ListenAndServe(":8080", r)
}

Para subir nossa API é só executar um go run main.go.

Com a API em pé, vamos testar ela com o comando curl http://localhost:8080. Se tudo certo, você deverá ter recebido um Hello world! como resposta.

Uma coisa interessante na nossa API é que, se executarmos o mesmo teste só que enviando um POST ao invés de GET (padrão do curl), nós vamos ter a mesma resposta.

Agora, vamos supor que não faça sentido essa rota receber um POST. Para que ela só atenda requisições do tipo GET, tudo o que precisamos fazer é adicionar um .Methods("GET") que o gorilla/mux irá tratar isso para nós.

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", HandleHello).Methods("GET")

    http.ListenAndServe(":8080", r)
}

Ao subir novamente a API e executar um curl -X POST http://localhost:8080 você verá que não teremos mais o Hello world! como resposta.

O .Methods() aceita uma lista de métodos, ou seja, caso seu endpoint vá tratar mais de um tipo de método, porém não todos os tipos, é possível utiliza-lo para fazer esse filtro. Exemplo:

r.HandleFunc("/products", HandleProducts).Methods("GET", "POST")

Para incrementar um pouquinho mais nossa API, vamos adicionar um parâmetro nesse nosso endpoint para que ele possa retornar algo como Hello Tiago! ao invés de só Hello world!.

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/{name}", HandleHello).Methods("GET")

    http.ListenAndServe(":8080", r)
}

Ao adicionar o {name}, se executarmos um curl http://localhost:8080, vamos receber um 404 como resposta pois nenhuma rota faz match com esse path.

Para finalizar nossa mudança, vamos extrair a variável passada no path e exibir ela na resposta.

func HandleHello(w http.ResponseWriter, rq *http.Request) {
    vars := mux.Vars(rq)
    fmt.Fprintf(w, "Hello %s!\n", vars["name"])
}

Suba a API novamente e teste com algo similar à curl http://localhost:8080/Tiago.

Show de bola, mas e se eu quiser retornar um JSON ao invés de texto puro?

Bom nesse caso precisamos adicionar o package encoding/json à nossa lista de imports e fazer mais uma pequena mudança.

func HandleHello(w http.ResponseWriter, rq *http.Request) {
    vars := mux.Vars(rq)

    w.Header().Add("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "message": fmt.Sprintf("Hello %s", vars["name"]),
    })
}

Levantando a API novamente e executando o mesmo teste, você verá que a resposta agora é um JSON. Se quiser pode adicionar um -vv ao final do comando curl para que tenha mais informações sobre a request/response.

Vale lembrar aqui também que o método Encode aceita uma interface{} como parâmetro, ou seja, ao invés de criar esse map[string]string, você pode passar uma struct e o método fará todo o trabalho de conversão.

Para finalizar nosso tutorial, vamos adicionar uma nova função para tratar um POST com o payload em JSON.

Para facilitar, vamos adicionar também uma struct.

type Person struct {
    Name string `json:"name"`
    Age  uint8  `json:"age"`
}

func HandlePerson(w http.ResponseWriter, rq *http.Request) {
    var p Person

    err := json.NewDecoder(rq.Body).Decode(&p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    fmt.Fprintf(w, "Nome: %s - Idade: %d\n", p.Name, p.Age)
}

Agora vamos adicionar uma nova rota na nossa função principal.

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/{name}", HandleHello).Methods("GET")
    r.HandleFunc("/person", HandlePerson).Methods("POST")

    http.ListenAndServe(":8080", r)
}

Levante a API e teste com o comando:

$ curl -X POST http://localhost:8080/person -H "application/json" -d '{"name": "Tiago Temporin", "age": 31}'

Você deve receber uma resposta similar à Nome: Tiago Temporin - Idade: 31.

Deixem suas dúvidas nos comentários.

Até a próxima!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Um comentário sobre “Implementando uma API com gorilla/mux

Deixe uma resposta