A linguagem de código GO permite a construção de códigos simples e eficientes. É uma linguagem aberta, compilada e estática e foi criada pela Google em 2007.

Neste tutorial vamos criar uma API RESTful para uma agenda de contactos. Como tal, não se trata de um artigo introdutório à linguagem GO — assumimos que o leitor já conhece a linguagem.

O Que é REST e RESTful?

REST é um protocolo HTTP e um padrão de arquitetura web. A sigla “REST” significa “Representational State Transfer”, ou, em português, algo como “Estado de Representação para Transferência”.

Este protocolo descreve seis restrições (para evitar confusões com traduções, iremos utilizar os nomes em inglês quando aplicável):

Interface Uniforme Cacheable Client-Server Stateless Code-on-Demand Layered System

O protocolo REST é composto por métodos tais como um URL base e tipos de média. Uma API RESTful obedece às seis regras do protocolo REST e utiliza requisições HTTP para a execução de operações CRUD (Create Read Update Delete).

Escrever a API RESTful

Antes de se poder começar a escrever a API é preciso que se configure o ambiente onde se vai trabalhar. Para isso, é necessário que se descarregue a versão mais recente da linguagem GO, aqui.

Depois de terminada a instalação, passará a estar disponível uma nova variável de ambiente no seu sistema ($GOPATH) . O código fonte da API vai ser criado na pasta $GOPATH .

É, portanto, necessário executar a seguinte linha no seu terminal:

$ mkdir -p $GOPATH/src/github/{seu usuario}/rest-api

O próximo comando vai criar uma pasta denominada api-rest no seu diretório GO:

$ cd rest-api

Depois, é necessário criar um arquivo com o nome main.go .

Criar as Rotas

Vamos utilizar o pacote mux para criar as rotas onde iremos receber as requisições HTTP. Para utilizar o pacote, é necessário utilizar o comando seguinte dentro da pasta rest-api :

rest-api$ go get github.com/gorilla/mux

Depois, no arquivo main.go :

package main



import (

"encoding/json"

"log"

"net/http"

"github.com/gorilla/mux"

)



// função principal

func main() {

router := mux.NewRouter()

log.Fatal(http.ListenAndServe(":8000", router))

}

Se executar esse comando, vai reparar que não terá qualquer tipo de resposta. Isso acontece porque ainda não registamos nenhuma rota (ou endpoint). Para este projeto vamos precisar das rotas seguintes:

/contato (GET) -> Todos os contactos da agenda (banco de dados)

(GET) -> Todos os contactos da agenda (banco de dados) /contato/{id} (GET) -> Retorna apenas um contacto

(GET) -> Retorna apenas um contacto /contato/{id} (POST) -> Cria um contacto novo

(POST) -> Cria um contacto novo /contato/{ID} (DELETE) -> Apaga um contacto

Temos, portanto, de escrever essas rotas:

// função principal

func main() {

router := mux.NewRouter()

router.HandleFunc("/contato", GetPeople).Methods("GET")

router.HandleFunc("/contato/{id}", GetPerson).Methods("GET")

router.HandleFunc("/contato/{id}", CreatePerson).Methods("POST")

router.HandleFunc("/contato/{id}", DeletePerson).Methods("DELETE")

log.Fatal(http.ListenAndServe(":8000", router))

}

E executar o código:

rest-api$ go build

rest-api$ ./rest-api

Não se preocupe quando o código acima der erro. É normal que aconteça porque ainda não criamos as funções que irão manusear as requisições. As funções que iremos criar estarão vazias ao início

func GetPeople(w http.ResponseWriter, r *http.Request) {}

func GetPerson(w http.ResponseWriter, r *http.Request) {}

func CreatePerson(w http.ResponseWriter, r *http.Request) {}

func DeletePerson(w http.ResponseWriter, r *http.Request) {}

Agora, quando se executar o código de novo, não teremos nenhum erro, no entanto os métodos ainda estão vazios.

Cada método vai receber dois parâmetros: w e r , que são do tipo http.ResponseWriter e *http.Request , respetivamente. Os dois parâmetros vão ser preenchidos sempre que uma das rotas for chamada.

Puxar os Dados da Agenda

Temos de escrever o código responsável por ir buscar todos os dados registados na nossa agenda, ainda sem qualquer contacto. Vamos registar alguns contactos manualmente, visto que os bancos de dados estão fora do âmbito deste tutorial.

Vamos adicionar uma estrutura (struct) Person (na qual deve pensar como sendo um objeto), uma estrutura Address e uma variável people para ser preenchida.

// arquivo main.go

...

type Person struct {

ID string `json:"id,omitempty"`

Firstname string `json:"firstname,omitempty"`

Lastname string `json:"lastname,omitempty"`

Address *Address `json:"address,omitempty"`

}

type Address struct {

City string `json:"city,omitempty"`

State string `json:"state,omitempty"`

}



var people []Person

...

Com as estruturas prontas, vamos adicionar alguns contactos manualmente ao slice de person:

// função main()

...



people = append(people, Person{ID: "1", Firstname: "John", Lastname: "Doe", Address: &Address{City: "City X", State: "State X"}})



people = append(people, Person{ID: "2", Firstname: "Koko", Lastname: "Doe", Address: &Address{City: "City Z", State: "State Y"}})



people = append(people, Person{ID: "3", Firstname: "Francis", Lastname: "Sunday"})



...

Depois, com os dados definidos, vamos buscar todos os contactos que estão no slice de “people”:

//main.go

...



func GetPeople(w http.ResponseWriter, r *http.Request) {

json.NewEncoder(w).Encode(people)

}



...

Quando executarmos novamente go build && ./rest-api e fizermos uma requisição com o postman, vamos ver uma resposta em json. Também podemos fazer o teste com o navegador, indo ao endereço localhost:8080/contato .

Puxar Só Um Contacto

Agora vamos tratar de escrever o código responsável por tirar apenas um contacto da nossa agenda:

// arquivo main.go

...



func GetPerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

for _, item := range people {

if item.ID == params["id"] {

json.NewEncoder(w).Encode(item)

return

}

}

json.NewEncoder(w).Encode(&Person{})

}



...

A função GetPerson faz um loop no nosso slice de pessoas para verificar se o id da requisição está presente no slice.

Finalizar Endpoints

Falta agora finalizar os outros endpoints (CreatePerson, DeletePerson):

func CreatePerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

var person Person

_ = json.NewDecoder(r.Body).Decode(&person)

person.ID = params["id"]

people = append(people, person)

json.NewEncoder(w).Encode(people)

}

func DeletePerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

for index, item := range people {

if item.ID == params["id"] {

people = append(people[:index], people[index+1]...)

break

}

json.NewEncoder(w).Encode(people)



}

}

O nosso código completo deverá ser algo semelhante a isto:

package main



import (

"encoding/json"

"github.com/gorilla/mux"

"log"

"net/http"

)



// "Person type" (tipo um objeto)

type Person struct {

ID string `json:"id,omitempty"`

Firstname string `json:"firstname,omitempty"`

Lastname string `json:"lastname,omitempty"`

Address *Address `json:"address,omitempty"`

}



type Address struct {

City string `json:"city,omitempty"`

State string `json:"state,omitempty"`

}



var people []Person



// GetPeople mostra todos os contatos da variável people

func GetPeople(w http.ResponseWriter, r *http.Request) {

json.NewEncoder(w).Encode(people)

}



// GetPerson mostra apenas um contato

func GetPerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

for _, item := range people {

if item.ID == params["id"] {

json.NewEncoder(w).Encode(item)

return

}

}

json.NewEncoder(w).Encode(&Person{})

}



// CreatePerson cria um novo contato

func CreatePerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

var person Person

_ = json.NewDecoder(r.Body).Decode(&person)

person.ID = params["id"]

people = append(people, person)

json.NewEncoder(w).Encode(people)

}



// DeletePerson deleta um contato

func DeletePerson(w http.ResponseWriter, r *http.Request) {

params := mux.Vars(r)

for index, item := range people {

if item.ID == params["id"] {

people = append(people[:index], people[index+1:]...)

break

}

json.NewEncoder(w).Encode(people)

}

}



// função principal para executar a api

func main() {

router := mux.NewRouter()



people = append(people, Person{ID: "1", Firstname: "John", Lastname: "Doe", Address: &Address{City: "City X", State: "State X"}})



people = append(people, Person{ID: "2", Firstname: "Koko", Lastname: "Doe", Address: &Address{City: "City Z", State: "State Y"}})



router.HandleFunc("/contato", GetPeople).Methods("GET")

router.HandleFunc("/contato/{id}", GetPerson).Methods("GET")

router.HandleFunc("/contato/{id}", CreatePerson).Methods("POST")

router.HandleFunc("/contato/{id}", DeletePerson).Methods("DELETE")



log.Fatal(http.ListenAndServe(":8000", router))

}

Teste

Podemos testar o nosso código através do Postman, ou através do navegador (desde que tenhamos uma extensão que formate o json para levar a resposta claramente).

Conclusão

Muito simplesmente, acabámos de construir uma API RESTful em GO. Apesar de termos utilizado dados manuais em vez de dados provenientes de um banco de dados, vimos como é que se criam rotas que fazem várias operações com json. Também criámos slices, que são os tipos de variáveis nativas em GO. Esquecemo-nos de alguma coisa? Se sim, deixe-nos saber com um comentário.