API en Go: REST, GraphQL y gRPC — qué elegir en 2025

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué Go es el lenguaje ideal para API?

\n\n

Cuando se trata de crear API de alta carga, Go (Golang) ocupa con confianza un lugar en la cima. Su naturaleza compilada, el soporte integrado de concurrencia a través de gorutinas y su sintaxis minimalista permiten escribir microservicios rápidos y confiables. Pero, ¿qué protocolo elegir para tu próximo proyecto: el clásico REST, el flexible GraphQL o el ultrarrápido gRPC?

\n\n

En este artículo no solo enumeraremos las diferencias — escribiremos código real en Go para cada enfoque, analizaremos benchmarks y daremos criterios claros de selección. ¡Allá vamos!

\n\n

API REST en Go: un clásico probado

\n\n

REST (Transferencia de Estado Representacional) sigue siendo el estándar de facto para API públicas. Go ofrece una excelente biblioteca estándar net/http, así como frameworks populares como gin y echo.

\n\n

Ejemplo de servidor REST en Go puro

\n\n

Empecemos con un servicio CRUD simple para gestionar tareas. Así es como se ve una API REST mínima en Go sin dependencias externas:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "log"\n    "net/http"\n    "sync"\n)\n\ntype Task struct {\n    ID   int    `json:"id"`\n    Name string `json:"name"`\n}\n\nvar (\n    tasks   = []Task{{ID: 1, Name: "Aprender Go"}}\n    mu      sync.Mutex\n    nextID  = 2\n)\n\nfunc getTasks(w http.ResponseWriter, r *http.Request) {\n    mu.Lock()\n    defer mu.Unlock()\n    w.Header().Set("Content-Type", "application/json")\n    json.NewEncoder(w).Encode(tasks)\n}\n\nfunc createTask(w http.ResponseWriter, r *http.Request) {\n    var task Task\n    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {\n        http.Error(w, err.Error(), http.StatusBadRequest)\n        return\n    }\n    mu.Lock()\n    task.ID = nextID\n    nextID++\n    tasks = append(tasks, task)\n    mu.Unlock()\n    w.WriteHeader(http.StatusCreated)\n    json.NewEncoder(w).Encode(task)\n}\n\nfunc main() {\n    http.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) {\n        switch r.Method {\n        case http.MethodGet:\n            getTasks(w, r)\n        case http.MethodPost:\n            createTask(w, r)\n        default:\n            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)\n        }\n    })\n    log.Println("API REST iniciada en :8080")\n    log.Fatal(http.ListenAndServe(":8080", nil))\n}\n
\n\n

Ventajas de REST en Go:

\n
    \n
  • Simplicidad y previsibilidad — cada endpoint devuelve una estructura de datos fija
  • \n
  • Excelente capacidad de almacenamiento en caché mediante cabeceras HTTP
  • \n
  • Amplio soporte de herramientas (Swagger, Postman)
  • \n
\n\n

Desventajas: Over-fetching y under-fetching de datos. Si el cliente solo necesita el nombre de la tarea, igualmente recibirá el JSON completo.

\n\n

GraphQL en Go: flexibilidad en las consultas

\n\n

GraphQL resuelve el problema de la redundancia de datos. El cliente decide qué campos necesita. La comunidad Go ofrece la potente biblioteca gqlgen, que genera código a partir del esquema.

\n\n

Ejemplo de servidor GraphQL con gqlgen

\n\n

Primero definimos el esquema schema.graphql:

\n\n
type Task {\n    id: ID!\n    name: String!\n}\n\ntype Query {\n    tasks: [Task!]!\n}\n\ntype Mutation {\n    createTask(name: String!): Task!\n}\n
\n\n

Ahora ejecutamos la generación y escribimos los resolvers en Go:

\n\n
package main\n\nimport (\n    "log"\n    "net/http"\n    "github.com/99designs/gqlgen/graphql/handler"\n    "github.com/99designs/gqlgen/graphql/playground"\n)\n\n// Suponemos que el código generado ya existe\ntype Resolver struct {\n    tasks []Task\n    mu    sync.Mutex\n}\n\nfunc (r *Resolver) Tasks(ctx context.Context) ([]Task, error) {\n    r.mu.Lock()\n    defer r.mu.Unlock()\n    return r.tasks, nil\n}\n\nfunc (r *Resolver) CreateTask(ctx context.Context, name string) (*Task, error) {\n    r.mu.Lock()\n    defer r.mu.Unlock()\n                

Blogs

Book Recommendations