GraphQL vs REST en Go: ¿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

GraphQL vs REST en Go: duelo de titanes, o cómo no equivocarse al elegir

\n\n

Si llevas al menos seis meses escribiendo en Go, seguramente te has topado con el debate eterno: ¿REST o GraphQL? Uno es el viejo y confiable "caballo de batalla", el otro es un "auto deportivo" moderno con un montón de opciones. Pero en el mundo de Go, donde el rendimiento y la simplicidad son una religión, la elección no siempre es obvia.

\n\n

En este artículo no solo enumeraremos ventajas y desventajas. Escribiremos código real en Go, mediremos tiempos de ejecución y analizaremos cuándo GraphQL es un tiro en el pie y cuándo es un pase directo al mundo de las API eficientes. ¡Vamos allá!

\n\n

REST en Go: estabilidad y previsibilidad

\n\n

REST es como un martillo viejo y probado. No brilla, pero clava los clavos perfectamente. En Go, las API REST se escriben con net/http o frameworks ligeros como gin y chi. Veamos un ejemplo simple:

\n\n
package main\n\nimport (\n    "encoding/json"\n    "net/http"\n)\n\ntype User struct {\n    ID   int    `json:"id"`\n    Name string `json:"name"`\n}\n\nfunc getUserHandler(w http.ResponseWriter, r *http.Request) {\n    user := User{ID: 1, Name: "Alexey"}\n    w.Header().Set("Content-Type", "application/json")\n    json.NewEncoder(w).Encode(user)\n}\n\nfunc main() {\n    http.HandleFunc("/api/user", getUserHandler)\n    http.ListenAndServe(":8080", nil)\n}
\n\n

¿Bonito? Sí. ¿Simple? Mucho. Pero, ¿qué pasa si el frontend solo necesita el nombre del usuario y nosotros enviamos todo el objeto? En REST esto se llama over-fetching — exceso de datos. Y en Go, donde estamos acostumbrados a controlar cada byte, esto puede ser frustrante.

\n\n

El problema N+1 en REST

\n\n

Supongamos que tenemos una lista de pedidos y para cada uno necesitamos el nombre del usuario. En REST, hacemos N consultas (lento) o creamos un endpoint separado /api/orders-with-users (montón de endpoints). Go maneja esto con consultas JOIN en la base de datos, pero a nivel de API sigue siendo tosco.

\n\n

GraphQL en Go: potencia y flexibilidad

\n\n

GraphQL es como una navaja suiza: un solo endpoint, y el cliente decide qué datos necesita. En Go, para esto está el framework gqlgen (de 99designs) — genera código a partir del esquema, muy en el espíritu de Go (menos código repetitivo, más acción).

\n\n
// schema.graphql\ntype Query {\n    user(id: Int!): User\n}\n\ntype User {\n    id: Int!\n    name: String!\n    orders: [Order!]!\n}\n\ntype Order {\n    id: Int!\n    total: Float!\n}
\n\n

Ahora la implementación en Go:

\n\n
package graph\n\nimport (\n    "context"\n    "fmt"\n)\n\ntype Resolver struct{}\n\nfunc (r *Resolver) User(ctx context.Context, args struct{ ID int }) (*User, error) {\n    // En la vida real — consulta a la BD\n    return &User{\n        ID:   args.ID,\n        Name: "María",\n        Orders: []*Order{\n            {ID: 1, Total: 250.50},\n        },\n    }, nil\n}
\n\n

El cliente solicita solo name — recibe solo name. Sin over-fetching. Pero hay un detalle: si el cliente solicita orders para 10 usuarios, y cada uno tiene 50 pedidos — la base de datos puede caer de rodillas. Esto se llama problema N+1 en GraphQL, y en Go se resuelve con DataLoaders.

\n\n

DataLoader — salvador del N+1

\n\n
import "github.com/graph-gophers/dataloader"\n\ntype UserLoader struct {\n    *dataloader.Loader\n}\n\nfunc NewUserLoader() *UserLoader {\n    return &UserLoader{\n        Loader: dataloader.NewBatchedLoader(func(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {\n            // Agrupamos consultas a la BD\n            userIDs := make([]int, len(keys))\n            for i, key := range keys {\n                userIDs[i] = key.Int()\n            }\n            users := batchFetchUsers(userIDs) // Una sola consulta SQL con WHERE IN\n            results := make([]*dataloader.Result, len(ke                

Blogs

Book Recommendations