Ir para o conteúdo

Criando uma REST API com framework Gin

De Wikiversidade

(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:

  • 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"})
}