Concepto clave
En el desarrollo de APIs REST con Go y gorilla/mux, el enrutador es el corazón que dirige las peticiones HTTP hacia los manejadores adecuados. Imagina un edificio de oficinas donde el recepcionista (el enrutador) decide a qué departamento (manejador) enviar a cada visitante (petición) basándose en su identificación (ruta y método HTTP). gorilla/mux extiende el enrutador estándar de Go, permitiendo definir rutas con parámetros dinámicos, validaciones y middleware de forma más expresiva.
Para una API de gestión de usuarios, necesitamos rutas que manejen operaciones CRUD (Crear, Leer, Actualizar, Eliminar) sobre recursos de usuario. Cada ruta debe seguir las convenciones REST: usar métodos HTTP apropiados (GET para leer, POST para crear, PUT/PATCH para actualizar, DELETE para eliminar) y devolver códigos de estado significativos (como 200 para éxito, 404 para no encontrado). La estructura del proyecto típica incluye un archivo principal (main.go) que configura el enrutador y los manejadores, separando la lógica de negocio en funciones o estructuras.
Cómo funciona en la práctica
Comienza creando un proyecto Go con go mod init y añadiendo la dependencia de gorilla/mux. Luego, en main.go, importa los paquetes necesarios y define una estructura User para representar los datos del usuario. Configura el enrutador usando mux.NewRouter() y registra rutas con métodos como router.HandleFunc(). Por ejemplo, para manejar una petición GET a /users/{id}, define una función que extraiga el ID de la URL, busque el usuario en memoria (o una base de datos) y devuelva una respuesta JSON.
Paso a paso: 1) Inicializa el enrutador, 2) Define las rutas y sus manejadores, 3) Implementa la lógica en los manejadores para procesar peticiones y respuestas, 4) Inicia el servidor HTTP. Usa herramientas como Postman o curl para probar cada endpoint, verificando que las respuestas sean correctas y los códigos de estado apropiados. Esto te permite iterar rápidamente y asegurar que la API funcione como se espera antes de añadir complejidad.
Código en acción
Aquí tienes un ejemplo funcional de una API simple para gestión de usuarios con gorilla/mux. Este código maneja usuarios almacenados en un slice en memoria, con operaciones básicas de CRUD. Copia y pega esto en un archivo main.go para ejecutarlo localmente.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var users []User
var currentID = 1
func getUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
func getUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for _, user := range users {
if user.ID == id {
json.NewEncoder(w).Encode(user)
return
}
}
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{"error": "Usuario no encontrado"})
}
func createUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user User
_ = json.NewDecoder(r.Body).Decode(&user)
user.ID = currentID
currentID++
users = append(users, user)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
func updateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
var updatedUser User
_ = json.NewDecoder(r.Body).Decode(&updatedUser)
for index, user := range users {
if user.ID == id {
users[index].Name = updatedUser.Name
users[index].Email = updatedUser.Email
json.NewEncoder(w).Encode(users[index])
return
}
}
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{"error": "Usuario no encontrado"})
}
func deleteUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for index, user := range users {
if user.ID == id {
users = append(users[:index], users[index+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{"error": "Usuario no encontrado"})
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/users", getUsers).Methods("GET")
router.HandleFunc("/users/{id}", getUser).Methods("GET")
router.HandleFunc("/users", createUser).Methods("POST")
router.HandleFunc("/users/{id}", updateUser).Methods("PUT")
router.HandleFunc("/users/{id}", deleteUser).Methods("DELETE")
log.Fatal(http.ListenAndServe(":8080", router))
}Antes de ejecutar, instala gorilla/mux con go get github.com/gorilla/mux. Luego, ejecuta go run main.go y prueba con curl: curl -X GET http://localhost:8080/users para ver la lista vacía inicial. Después, crea un usuario: curl -X POST -H "Content-Type: application/json" -d '{"name":"Juan","email":"[email protected]"}' http://localhost:8080/users. Esto te dará una respuesta JSON con el usuario creado, incluyendo un ID asignado.
Errores comunes
- No validar entradas: En el ejemplo anterior, no validamos que el cuerpo de la petición POST tenga los campos requeridos. Esto puede llevar a datos corruptos. Solución: Añade validaciones básicas, como verificar que
NameyEmailno estén vacíos antes de procesar. - Olvidar configurar headers de contenido: Si no estableces
Content-Type: application/jsonen las respuestas, los clientes pueden no interpretar correctamente los datos. Siempre incluyew.Header().Set("Content-Type", "application/json")al inicio de cada manejador que devuelva JSON. - Manejo inadecuado de errores: En el código, usamos
_ = json.NewDecoder(r.Body).Decode(&user)ignorando errores. Esto puede causar pánicos si el JSON es inválido. Solución: Maneja el error y devuelve un código 400 Bad Request con un mensaje claro. - Uso de slices en memoria para producción: Almacenar usuarios en un slice volátil es solo para demostración; en producción, usa una base de datos. Refactoriza para integrar algo como PostgreSQL o MongoDB, manteniendo la misma interfaz de API.
- No limpiar recursos: En una API real, asegúrate de cerrar conexiones o liberar recursos. Aunque este ejemplo es simple, es un hábito clave para evitar fugas de memoria en sistemas de alto rendimiento.
Checklist de dominio
- ¿Puedes crear un enrutador con gorilla/mux y definir al menos 5 rutas RESTful para un recurso?
- ¿Sabes extraer parámetros de URL (como
{id}) y convertirlos a tipos Go adecuados? - ¿Eres capaz de manejar peticiones POST, decodificar JSON del cuerpo y validar datos básicos?
- ¿Puedes devolver respuestas JSON con códigos de estado HTTP apropiados (200, 201, 404, etc.)?
- ¿Entiendes cómo probar la API con herramientas como curl o Postman, verificando cada endpoint?
- ¿Sabes identificar y corregir errores comunes, como falta de validación o headers incorrectos?
- ¿Puedes explicar por qué el almacenamiento en memoria no es adecuado para producción y qué alternativas usarías?
Extender la API de usuarios con validación y logging
En este ejercicio, mejorarás la API de gestión de usuarios añadiendo validación de datos y logging básico. Sigue estos pasos:
- Clona el código base: Usa el ejemplo proporcionado en la lección como punto de partida. Asegúrate de que funcione localmente en
localhost:8080. - Añade validación en el manejador POST: Modifica la función
createUserpara validar que los camposNameyEmailno estén vacíos y que el email tenga un formato básico (contenga '@'). Si la validación falla, devuelve un código de estado 400 con un mensaje de error en JSON. - Implementa logging: Añade logging para registrar cada petición entrante. Usa el paquete
logde Go para imprimir en consola el método HTTP, la ruta y el código de estado de respuesta. Haz esto en un middleware o directamente en cada manejador. - Refactoriza el almacenamiento: Cambia el slice en memoria por un map de Go (por ejemplo,
map[int]User) para mejorar la eficiencia en búsquedas por ID. Actualiza las funciones CRUD para usar este map. - Prueba las mejoras: Usa curl o Postman para enviar peticiones válidas e inválidas, verificando que las validaciones y el logging funcionen correctamente. Por ejemplo, prueba crear un usuario sin nombre y observa la respuesta de error.
Al final, deberías tener una API más robusta y lista para integrar en un entorno de desarrollo real.
Pistas- Para validar el formato de email, puedes usar una expresión regular simple o verificar que la cadena contenga '@' y un punto después.
- Considera crear una función auxiliar para logging que puedas llamar desde cada manejador, evitando duplicar código.
- Al usar un map, recuerda manejar el acceso concurrente si planeas usar gorutinas en el futuro; por ahora, un map simple es suficiente para este ejercicio.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.