Como Criar uma API REST com Go
Go é perfeito para APIs: rápido, simples, e a biblioteca padrão já inclui tudo que você precisa.
O que Vamos Construir
Uma API de tarefas (TODO) com:
- Listar todas as tarefas
- Criar nova tarefa
- Buscar tarefa por ID
- Atualizar tarefa
- Deletar tarefa
Setup do Projeto
mkdir todo-api
cd todo-api
go mod init todo-api
API Básica com net/http
Crie main.go:
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"sync"
)
// Modelo
type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
// "Banco de dados" em memória
var (
tasks = make(map[int]Task)
nextID = 1
mu sync.Mutex
)
func main() {
http.HandleFunc("/tasks", handleTasks)
http.HandleFunc("/tasks/", handleTaskByID)
fmt.Println("Servidor rodando em http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
Handlers
Listar e Criar Tarefas
func handleTasks(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
listTasks(w, r)
case "POST":
createTask(w, r)
default:
http.Error(w, "Método não permitido", http.StatusMethodNotAllowed)
}
}
func listTasks(w http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
taskList := make([]Task, 0, len(tasks))
for _, task := range tasks {
taskList = append(taskList, task)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(taskList)
}
func createTask(w http.ResponseWriter, r *http.Request) {
var task Task
if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
mu.Lock()
task.ID = nextID
nextID++
tasks[task.ID] = task
mu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(task)
}
Buscar, Atualizar e Deletar
func handleTaskByID(w http.ResponseWriter, r *http.Request) {
// Extrai ID da URL: /tasks/123
idStr := r.URL.Path[len("/tasks/"):]
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "ID inválido", http.StatusBadRequest)
return
}
switch r.Method {
case "GET":
getTask(w, r, id)
case "PUT":
updateTask(w, r, id)
case "DELETE":
deleteTask(w, r, id)
default:
http.Error(w, "Método não permitido", http.StatusMethodNotAllowed)
}
}
func getTask(w http.ResponseWriter, r *http.Request, id int) {
mu.Lock()
task, ok := tasks[id]
mu.Unlock()
if !ok {
http.Error(w, "Tarefa não encontrada", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(task)
}
func updateTask(w http.ResponseWriter, r *http.Request, id int) {
mu.Lock()
defer mu.Unlock()
if _, ok := tasks[id]; !ok {
http.Error(w, "Tarefa não encontrada", http.StatusNotFound)
return
}
var task Task
if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
task.ID = id
tasks[id] = task
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(task)
}
func deleteTask(w http.ResponseWriter, r *http.Request, id int) {
mu.Lock()
defer mu.Unlock()
if _, ok := tasks[id]; !ok {
http.Error(w, "Tarefa não encontrada", http.StatusNotFound)
return
}
delete(tasks, id)
w.WriteHeader(http.StatusNoContent)
}
Testando a API
Execute o servidor:
go run main.go
Com cURL
# Criar tarefa
curl -X POST http://localhost:8080/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Aprender Go", "completed": false}'
# Listar tarefas
curl http://localhost:8080/tasks
# Buscar tarefa
curl http://localhost:8080/tasks/1
# Atualizar tarefa
curl -X PUT http://localhost:8080/tasks/1 \
-H "Content-Type: application/json" \
-d '{"title": "Aprender Go", "completed": true}'
# Deletar tarefa
curl -X DELETE http://localhost:8080/tasks/1
Usando Gin (Framework Popular)
Para projetos maiores, frameworks como Gin simplificam o código:
go get -u github.com/gin-gonic/gin
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
var tasks = []Task{}
var nextID = 1
func main() {
r := gin.Default()
r.GET("/tasks", listTasks)
r.POST("/tasks", createTask)
r.GET("/tasks/:id", getTask)
r.PUT("/tasks/:id", updateTask)
r.DELETE("/tasks/:id", deleteTask)
r.Run(":8080")
}
func listTasks(c *gin.Context) {
c.JSON(http.StatusOK, tasks)
}
func createTask(c *gin.Context) {
var task Task
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
task.ID = nextID
nextID++
tasks = append(tasks, task)
c.JSON(http.StatusCreated, task)
}
// ... outros handlers similares
Boas Práticas
1. Estrutura de Projeto
todo-api/
├── main.go
├── handlers/
│ └── tasks.go
├── models/
│ └── task.go
├── middleware/
│ └── logging.go
└── go.mod
2. Middleware de Logging
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
3. Tratamento de Erros
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
}
func respondWithError(w http.ResponseWriter, code int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(APIError{Code: code, Message: message})
}
Deploy
Docker
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
docker build -t todo-api .
docker run -p 8080:8080 todo-api
Próximos Passos
- Adicionar banco de dados (PostgreSQL, MySQL)
- Implementar autenticação (JWT)
- Adicionar testes automatizados
- Documentar com Swagger
Veja Também
Última atualização: Janeiro 2026
Continue aprendendo
- Go com PostgreSQL — Conecte sua API ao banco de dados
- Testes em Go — Teste sua API com table-driven tests
- Autenticação JWT — Adicione autenticação à sua API
- Go com Docker — Faça deploy da sua API em containers
- Tratamento de Erros — Error handling profissional
- Vagas Back-end Go — Encontre vagas de backend