Introdução: por que Go para API REST?
\nGo (Golang) é uma linguagem compilada moderna com suporte embutido para concorrência e alto desempenho. Uma API REST em Go funciona rápido, consome pouca memória e é facilmente escalável. Em 30 minutos, você criará um serviço REST completo com requisições GET, POST, PUT e DELETE, usando apenas a biblioteca padrão.
\nNeste guia, escreveremos uma API para gerenciar uma lista de tarefas (Todo app). Você aprenderá como configurar o roteamento, processar JSON, adicionar middleware para logging e retornar erros corretamente. Sem frameworks de terceiros — apenas Go puro!
\n\n1. Configuração do projeto e estrutura
\nCrie um novo diretório para o projeto e inicialize o módulo Go:
\nmkdir go-todo-api\ncd go-todo-api\ngo mod init todo-api\nA estrutura do projeto será simples:
\n- \n
main.go— ponto de entrada e inicialização do servidor \nhandlers.go— manipuladores de requisições \nmodels.go— estruturas de dados \nmiddleware.go— middleware \n
Crie o arquivo models.go com a estrutura da tarefa:
package main\n\ntype Task struct {\n ID int `json:"id"`\n Title string `json:"title"`\n Completed bool `json:"completed"`\n}\nArmazenaremos as tarefas em memória (slice tasks) para simplificar. Em um projeto real, use um banco de dados.
\n\n2. Roteamento e manipuladores
\nGo usa o pacote embutido net/http. Vamos criar o arquivo handlers.go com os principais manipuladores.
2.1. GET /tasks — obter todas as tarefas
\npackage main\n\nimport (\n "encoding/json"\n "net/http"\n "strconv"\n)\n\nvar tasks = []Task{}\nvar nextID = 1\n\nfunc getTasks(w http.ResponseWriter, r *http.Request) {\n w.Header().Set("Content-Type", "application/json")\n json.NewEncoder(w).Encode(tasks)\n}\n2.2. POST /tasks — criar uma nova tarefa
\nfunc createTask(w http.ResponseWriter, r *http.Request) {\n var newTask Task\n if err := json.NewDecoder(r.Body).Decode(&newTask); err != nil {\n http.Error(w, "Invalid JSON", http.StatusBadRequest)\n return\n }\n newTask.ID = nextID\n nextID++\n tasks = append(tasks, newTask)\n w.Header().Set("Content-Type", "application/json")\n w.WriteHeader(http.StatusCreated)\n json.NewEncoder(w).Encode(newTask)\n}\n2.3. PUT /tasks/{id} — atualizar uma tarefa
\nfunc updateTask(w http.ResponseWriter, r *http.Request) {\n idStr := r.PathValue("id")\n id, err := strconv.Atoi(idStr)\n if err != nil {\n http.Error(w, "Invalid task ID", http.StatusBadRequest)\n return\n }\n \n var updatedTask Task\n if err := json.NewDecoder(r.Body).Decode(&updatedTask); err != nil {\n http.Error(w, "Invalid JSON", http.StatusBadRequest)\n return\n }\n \n for i, t := range tasks {\n if t.ID == id {\n tasks[i].Title = updatedTask.Title\n tasks[i].Completed = updatedTask.Completed\n w.Header().Set("Content-Type", "application/json")\n json.NewEncoder(w).Encode(tasks[i])\n return\n }\n }\n http.Error(w, "Task not found", http.StatusNotFound)\n}\n2.4. DELETE /tasks/{id} — excluir uma tarefa
\nfunc deleteTask(w http.ResponseWriter, r *http.Request) {\n idStr := r.PathValue("id")\n id, err := strconv.Atoi(idStr)\n if err != nil {\n http.Error(w, "Invalid task ID", http.StatusBadRequest)\n return\n }\n \n for i, t := range tasks {\n if t.ID == id {\n tasks = append(tasks[:i], tasks[i+1:]...)\n w.WriteHeader(http.StatusNoContent)\n return\n }\n }\n http.Error(w, "Task not found", http.StatusNotFound)\n}\nObserve