GraphQL no vino a sustituir a REST, y a estas alturas está claro que no lo va a hacer: cada uno resuelve bien problemas distintos, y muchas empresas grandes usan los dos a la vez, cada uno donde encaja mejor. La decisión no es binaria — depende de cuántos clientes distintos consume tu API y de qué tan relacionados están tus datos.
REST: simple, pero rígido
A favor: extremadamente simple de entender (GET /users/123 devuelve el usuario), el caching es trivial porque los proxies y CDNs lo hacen de forma automática, la semántica HTTP (200, 404, 500) es universal, y depurar con curl o el navegador no podría ser más directo.
En contra: over-fetching —pides un usuario y te llegan cien campos cuando solo necesitabas dos—, under-fetching —necesitas el usuario y sus posts, y eso son dos peticiones separadas—, y el versionado (/v1/users vs /v2/users) tiende a multiplicar endpoints con el tiempo.
Tiene sentido cuando el CRUD es simple, hay un solo tipo de cliente (una web, por ejemplo), el caching importa de verdad, y la capacidad de depurar rápido es una prioridad.
GraphQL: flexible, pero más complejo de operar
A favor: un único endpoint donde el cliente pide exactamente los campos que necesita, un schema fuertemente tipado que sirve como fuente única de verdad, y documentación automática vía introspección.
En contra: la curva de aprendizaje para el equipo no es trivial, el caching HTTP estándar no funciona porque las peticiones son POST, es fácil escribir un resolver que dispare cientos de queries sin darte cuenta (el problema N+1, ver más abajo), y el monitoring es más difícil porque todas las peticiones llegan al mismo endpoint sin diferenciación aparente en los logs.
Tiene sentido cuando hay varios clientes con necesidades de datos distintas (web, móvil, escritorio), los datos son altamente relacionales, el schema evoluciona con frecuencia, o necesitas subscripciones en tiempo real.
Cómo decidir
- ¿Cuántos clientes distintos consumen la API? Uno solo: REST es suficiente. Dos o más con necesidades distintas: GraphQL empieza a justificarse.
- ¿Los datos son muy relacionales (usuario → posts → comentarios → reacciones)? Si sí, GraphQL suele encajar mejor.
- ¿El backend cambia rápido y necesitas mantener compatibilidad hacia atrás sin versionar endpoints? GraphQL tiene ventaja aquí.
- ¿El caching HTTP es una prioridad real? REST lo resuelve gratis; GraphQL lo complica.
Ante la duda, REST suele ser la opción más simple de mantener.
Un ejemplo concreto: móvil pidiendo usuario, posts y comentarios
Con REST, esto normalmente son varias peticiones encadenadas —una para el usuario, otra para sus posts, otra más para los comentarios de cada post— cada una devolviendo todos los campos del recurso, aunque la app solo necesite un par de ellos. Con GraphQL, la misma necesidad se resuelve en una sola petición, pidiendo exactamente los campos requeridos:
query {
user(id: 123) {
name
email
posts(limit: 10) {
title
createdAt
comments(limit: 5) {
text
author { name }
}
}
}
}
El resultado suele ser una respuesta bastante más ligera que la suma de las peticiones REST equivalentes, con el beneficio añadido de que si el cliente necesita un campo nuevo (el nombre del autor de un comentario, por ejemplo), es un cambio de una línea en la query, sin tocar el backend.
Implementación básica con Apollo Server
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
author: User!
comments: [Comment!]!
}
type Comment {
id: ID!
text: String!
author: User!
}
type Query {
user(id: ID!): User
posts: [Post!]!
}
`;
const resolvers = {
Query: {
user: (_, { id }) => db.users.findById(id),
posts: () => db.posts.findAll(),
},
User: {
posts: (user) => db.posts.findByUserId(user.id),
},
Post: {
author: (post) => db.users.findById(post.author_id),
comments: (post) => db.comments.findByPostId(post.id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen(4000);
El problema N+1 (y cómo evitarlo)
Si el resolver de comentarios hace una query por cada post individualmente, una lista de cien posts genera cien queries adicionales además de la inicial:
SELECT * FROM posts; -- 1 query
SELECT * FROM comments WHERE post_id = 1; -- por cada post...
SELECT * FROM comments WHERE post_id = 2;
-- ...cien veces
La solución estándar es agrupar esas llamadas con DataLoader, que junta las peticiones pendientes en una sola query por lote:
const DataLoader = require('dataloader');
const commentLoader = new DataLoader(async (postIds) => {
const comments = await db.comments.findByPostIds(postIds);
return postIds.map(id => comments.filter(c => c.post_id === id));
});
const resolvers = {
Post: {
comments: (post) => commentLoader.load(post.id),
},
};
// Ahora: 1 query para posts + 1 query agrupada para todos los comentarios
Caching: el punto débil de GraphQL
El caching HTTP tradicional funciona con cabeceras como Cache-Control sobre peticiones GET. Como GraphQL usa POST para casi todo, ese mecanismo no aplica directamente. Las alternativas habituales son: exponer también una variante GET para queries concretas, usar consultas persistentes (identificadas por hash en vez de enviar el texto completo cada vez), o apoyarse en el caché del lado del cliente que Apollo Client trae integrado. En la práctica, lo más común es aceptar que GraphQL es más difícil de cachear a nivel HTTP y resolverlo del lado del cliente.
Monitoring: diferenciar peticiones que llegan al mismo endpoint
Con REST, un log de GET /users/123 ya te dice qué se pidió. Con GraphQL, todo llega como POST /graphql, así que hace falta loguear explícitamente el nombre de la operación y su duración:
server.on('didResolveOperation', ({ request, document }) => {
console.log(`GraphQL query: ${document.definitions[0].name?.value}`);
});
Migrar de REST a GraphQL sin reescribir todo
- Crea un gateway GraphQL que envuelva las APIs REST existentes
- Migra primero el cliente que más se beneficia (normalmente móvil, por el ahorro de datos)
- Deja que el resto de clientes sigan en REST mientras tanto
- Evalúa si vale la pena migrar el resto solo cuando ambos caminos ya funcionen en paralelo
Herramientas
Apollo Server es la opción más extendida para levantar un servidor GraphQL. GraphQL Playground sirve para probar queries de forma interactiva. Hasura genera una API GraphQL instantánea sobre una base de datos existente.
En resumen
REST cuando el equipo es pequeño, los clientes son pocos, el CRUD es simple y el caching importa. GraphQL cuando hay varios clientes con necesidades distintas, los datos son muy relacionales, y el over-fetching es un problema real y medible. Y en muchos casos, la mejor respuesta es un híbrido: REST para lo simple, GraphQL para lo complejo, ambos conviviendo sin problema.
Fuentes:






Comentarios (0)
Deja un comentario
No hay comentarios aún. ¡Sé el primero en comentar!