Introducción: ¿por qué Go para API REST?
\nGo (Golang) es un lenguaje compilado moderno con soporte integrado para concurrencia y alto rendimiento. Una API REST en Go funciona rápido, consume poca memoria y se escala fácilmente. En 30 minutos creará un servicio REST completo con solicitudes GET, POST, PUT y DELETE, utilizando solo la biblioteca estándar.
\nEn esta guía escribiremos una API para gestionar una lista de tareas (aplicación Todo). Aprenderá cómo configurar el enrutamiento, manejar JSON, agregar middleware para registro y devolver errores correctamente. Sin frameworks externos — ¡solo Go puro!
\n\n1. Configuración del proyecto y estructura
\nCree un nuevo directorio para el proyecto e inicialice el módulo Go:
\nmkdir go-todo-api\ncd go-todo-api\ngo mod init todo-api\nLa estructura del proyecto será simple:
\n- \n
main.go— punto de entrada e inicio del servidor \nhandlers.go— manejadores de solicitudes \nmodels.go— estructuras de datos \nmiddleware.go— middleware \n
Cree el archivo models.go con la estructura de la tarea:
package main\n\ntype Task struct {\n ID int `json:"id"`\n Title string `json:"title"`\n Completed bool `json:"completed"`\n}\nAlmacenaremos las tareas en memoria (slice tasks) por simplicidad. En un proyecto real, use una base de datos.
\n\n2. Enrutamiento y manejadores
\nGo utiliza el paquete integrado net/http. Crearemos el archivo handlers.go con los manejadores principales.
2.1. GET /tasks — obtener todas las tareas
\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 — crear una nueva tarea
\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} — actualizar una tarea
\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} — eliminar una tarea
\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}\nPreste atenci