Criando uma REST API com framework Gin
(Página Minimal e Eternamente em construção...)
Iremos explorar conceitos de um servidor, de uma REST API e de como construir uma usando a linguagem GO juntamente ao framework Gin.
O que é um servidor?
[editar | editar código]Antes de mais nada, vamos definir primeiro o que é um servidor. Um servidor é um programa de computador que provê funcionalidades para outros programas (clientes). No contexto de desenvolvimento web, servidores respondem a requisições de rede de clientes (navegadores, aplicativos, etc.).
O protocolo fundamental da comunicação de dados na web é o HTTP (Hypertext Transfer Protocol). O HTTP define como as mensagens deverão ser formatadas e transmitidas e como clientes e servidores vão responder aos comandos.
Os principais componentes do protocolo HTTP são:
- URL: Identifica recursos (e.g., https://example.com/api/users)
- Methods: Definem ações (GET, POST, PUT, DELETE, etc.)
- Headers: Metadados das requisições e das respostas.
- Status Codes: Indica o resultado das requisições (200 OK, 404 Not Found, etc.)
- Body: Contém os dados que são enviados e recebidos.
O que é uma REST API?
[editar | editar código]REST (Representational State Transfer) é um estilo de arquitetura que foi desenvolvido para aplicações web. RESTful APIs usam requisições HTTP para executar operações CRUD (Create, Read, Update, Delete).
Princípios da arquitetura REST:
- Stateless: Cada requisições contém todos os dados necessários para que a operação seja realizada.
- Baseada em recursos: Recursos identificados por URLs.
- Métodos padrões: Utilizar os métodos HTTP de maneira apropriada (GET, POST, PUT, DELETE)
- Representações múltiplas: Recursos podem ser representados de maneiras diferentes (JSON, XML)
Os recursos que a REST API provisiona são baseados no conceitos de endpoints. Cada endpoint é uma URL específica mais um método HTTP, que representa um recurso ou uma coleção de recursos, sendo basicamente o ponto de contato entre o servidor e o cliente.
Anatomia de um endpoint:
[editar | editar código]https://api.example.com/v1/resources/:id
- URL base: https://api.example.com
- Versão da API : /v1
- Recursos: /resources
Padrões comuns de endpoints:
[editar | editar código]Endpoints de coleções: Acessam múltiplos recursos
[editar | editar código]- GET /users - Lista todos os usuários
Endpoints singleton: Acessam um recurso específico
[editar | editar código]- GET /users/123 - Retorna dados do usuário identificado por 123
- PUT /users/123 - Atualiza usuário identificado por 123
- DELETE /users/123 - Deleta usuário identificado por 123
Endpoints de relações: Acessam recursos relacionados
[editar | editar código]- GET /users/123/orders - Lista todas as ordens do usuário identificado por 123
- POST /users/123/orders - Cria nova ordem para usuário 123
Criando uma REST API utilizando Gin framework
[editar | editar código]Gin é um framework web para linguagem GO que fornece alguns utilitários para facilitar a criação de uma aplicação web.
Criando um novo projeto
[editar | editar código]Nosso primeiro passo será criar um novo projeto. Para isso, certifique-se de ter a linguagem GO já instalada na sua máquina. Abra um terminal e execute os seguintes comandos:
mkdir rest-api-go
cd rest-api-go
go mod init rest-api-go
O comando go mod init criar um novo módulo. Todo código GO é agrupado em pacotes (packages) e esses pacotes são agrupados em módulos (module). O módulo vai especificar dependências que são necessárias para executá-lo, incluindo a versão da linguagem GO. Após isso, iremos instalar o framework Gin:
go get github.com/gin-gonic/gin
Criando um servidor básico:
[editar | editar código]Crie um arquivo com nome main.go. Nesse arquivo, vamos adicionar o seguinte código:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// Inicializa o servidor
router := gin.Default()
// Definimos uma rota
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
// Inicia o servidor
router.Run(":8080")
}
No exemplo acima, nos estamos requisitando o pacote gin no nosso código. Utilizando o pacote, inicializamos um novo servidor e então definimos uma rota /ping, utilizando o método HTTP GET. Qualquer requisição nesse recurso vai ser respondida com o JSON "{ "message": "pong"}". Após isso, nós vinculamos nosso servidor a porta 8080.
Um exemplo mais complexo:
[editar | editar código]Vamos criar uma REST API simples de gerenciamento de usuários:
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// Representa os dados do usuário
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// Criamos um vetor em memória para simbolizar uma fonte de dados
var users = []User{
{ID: 1, Name: "Victor Pereira Feitosa", Email: "victor.pereira.feitosa@usp.br"},
{ID: 2, Name: "Rodrigo Dassie Paneto", Email: "rdassiepaneto@usp.br"},
}
func main() {
router := gin.Default()
// Agrupamos rotas de usuários
userRoutes := router.Group("/api/users")
{
// Lista todoso usuários
userRoutes.GET("", getAllUsers)
// Usuário por ID
userRoutes.GET("/:id", getUserByID)
// Cria um novo usuário
userRoutes.POST("", createUser)
// Atualiza um usuário
userRoutes.PUT("/:id", updateUser)
// Deleta um usuário
userRoutes.DELETE("/:id", deleteUser)
}
router.Run(":8080")
}
// Funções que vão executar as operações
func getAllUsers(c *gin.Context) {
c.JSON(http.StatusOK, users)
}
func getUserByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"erro": "ID inválido"})
return
}
for _, user := range users {
if user.ID == id {
c.JSON(http.StatusOK, user)
return
}
}
c.JSON(http.StatusNotFound, gin.H{"erro": "Usuário não encontrado"})
}
func createUser(c *gin.Context) {
var newUser User
if err := c.ShouldBindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"erro": err.Error()})
return
}
// Cria um novo ID para o usuário e atualiza o vetor
newUser.ID = len(users) + 1
users = append(users, newUser)
c.JSON(http.StatusCreated, newUser)
}
func updateUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"erro": "ID inválido"})
return
}
var updatedUser User
if err := c.ShouldBindJSON(&updatedUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"erro": err.Error()})
return
}
for i, user := range users {
if user.ID == id {
updatedUser.ID = id
users[i] = updatedUser
c.JSON(http.StatusOK, updatedUser)
return
}
}
c.JSON(http.StatusNotFound, gin.H{"erro": "Uusário não encontrado"})
}
func deleteUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"erro": "ID inválido"})
return
}
for i, user := range users {
if user.ID == id {
users = append(users[:i], users[i+1:]...)
c.JSON(http.StatusOK, gin.H{"mensagem": "Uusário deletado com sucesso"})
return
}
}
c.JSON(http.StatusNotFound, gin.H{"erro": "Usuário não encontrado"})
}