GraphQL vs REST — Cuándo Elegir y Cómo Migrar
Guía de decisión comparando GraphQL y REST APIs: casos de uso, rendimiento, caching, herramientas y estrategias de migración para equipos de ingeniería.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Visión General
REST ha sido el estándar de facto para APIs web durante más de una década. GraphQL, desarrollado por Facebook en 2012, ofrece un enfoque diferente: en lugar de múltiples endpoints fijos, el cliente solicita exactamente los datos que necesita en una sola consulta. La elección entre ambos no es dogmática — depende de tus clientes, tu equipo, y las restricciones de rendimiento. Esta guía compara casos de uso, trade-offs, caching, herramientas y estrategias de migración.
Cuándo Usar
Usa esta guía cuando:
- Estás diseñando una nueva API y debes elegir entre GraphQL y REST
- Tu API REST actual sufre de over-fetching, under-fetching o explosión de endpoints
- Consideras migrar de REST a GraphQL y necesitas una estrategia de transición
Solución
Comparación Fundamental
| Dimensión | REST | GraphQL |
|---|---|---|
| Modelo de datos | Múltiples recursos con endpoints fijos | Un solo endpoint con schema tipado |
| Solicitud del cliente | URL + método HTTP + headers | Query/mutation/subscription en body |
| Respuesta | Formato fijo definido por servidor | Formato flexible definido por cliente |
| Over-fetching | Común (obtienes todo el recurso) | Eliminado (solicitas campos específicos) |
| Under-fetching | Común (múltiples requests para datos relacionados) | Eliminado (resolución anidada en un solo request) |
| Versionado | v1, v2 en URL o headers | Evolución del schema sin versionado |
| Caching | HTTP nativo (Cache-Control, ETag) | Requiere cache de aplicación (Apollo, Relay) |
| Herramientas | Swagger/OpenAPI, Postman | Apollo Studio, GraphiQL, codegen |
| Curva de aprendizaje | Baja (conceptos HTTP familiares) | Media-alta (schema, resolvers, N+1) |
Ejemplo REST vs GraphQL
# REST: Múltiples requests para datos relacionados
GET /users/42
GET /users/42/posts
GET /users/42/followers
# Respuestas: 3 requests, datos potencialmente no necesarios
# GraphQL: Un solo request con datos exactos
query {
user(id: 42) {
name
email
posts(limit: 5) {
title
createdAt
}
followers {
name
}
}
}
# Respuesta: 1 request, solo campos solicitados
Implementación de Resolver con DataLoader (N+1)
// Resolver GraphQL con DataLoader para evitar N+1
const DataLoader = require('dataloader');
const userLoader = new DataLoader(async (userIds) => {
const users = await db.users.findMany({
where: { id: { in: userIds } }
});
return userIds.map(id => users.find(u => u.id === id));
});
const resolvers = {
Post: {
author: (post) => userLoader.load(post.authorId)
},
Query: {
posts: () => db.posts.findMany()
}
};
Caching en GraphQL
// Cache de resolvers con Redis
const { Redis } = require('ioredis');
const redis = new Redis();
const cachedResolver = (key, ttl, resolver) => async (parent, args, context) => {
const cacheKey = `${key}:${JSON.stringify(args)}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const result = await resolver(parent, args, context);
await redis.setex(cacheKey, ttl, JSON.stringify(result));
return result;
};
const resolvers = {
Query: {
user: cachedResolver('user', 300, async (_, { id }) => {
return db.users.findById(id);
})
}
};
Explicación
REST funciona mejor cuando tus recursos son estáticos y bien definidos y tus clientes consumen datos de manera predecible. El caching HTTP funciona automáticamente con navegadores, CDNs y proxies. GraphQL brilla cuando tienes múltiples clientes con necesidades de datos diferentes — una app móvil que necesita resúmenes, una app web que necesita detalles, un dashboard que necesita agregaciones.
El problema N+1 es el mayor riesgo de GraphQL. Si un query solicita 100 posts y cada post tiene un autor, un resolver naive hará 101 queries a la base de datos. DataLoader resuelve esto con batching: acumula IDs durante un tick del event loop, luego hace una sola query WHERE IN. Sin DataLoader, GraphQL es más lento que REST.
El versionado en GraphQL se maneja mediante evolución del schema: agregar campos es no-ruptor; deprecar campos en lugar de eliminarlos. Esto elimina el caos de /v1, /v2, pero requiere disciplina del equipo. El schema actúa como contrato; romperlo es un breaking change detectable por herramientas.
Variantes
| Situación | Recomendación | Razonamiento |
|---|---|---|
| API pública simple | REST | Caching HTTP, familiaridad, menor barrera de entrada |
| Múltiples clientes (móvil + web + dashboard) | GraphQL | Diferentes necesidades de datos sin proliferación de endpoints |
| Microservicios con agregación | GraphQL + Federación | Unifica múltiples servicios bajo un solo schema |
| Alto throughput, baja latencia | REST + gRPC | Caching HTTP y wire format binario |
| Migración gradual desde REST | GraphQL como capa de agregación | Deja REST intacto, agrega GraphQL sobre él |
| Real-time updates | GraphQL Subscriptions | WebSocket nativo para eventos push |
Mejores Prácticas
- Usa DataLoader siempre que haya relaciones en tu schema; sin batching, GraphQL es lentísimo
- Limita la profundidad de query (maxDepth) y complejidad (complexity score) para prevenir DoS
- Instrumenta tiempo de resolución por campo para identificar resolvers lentos
- Mantén el schema como tu contrato público; documenta cada tipo y campo
- Usa persisted queries en producción para reducir payload de query strings
Errores Comunes
- Migrar todo a GraphQL sin agregar DataLoader; el N+1 mata el rendimiento
- Exponer mutaciones complejas que modifican múltiples recursos; preferir atomicidad
- Ignorar rate limiting de queries; un query profundo puede DoSearte fácilmente
- Tratar GraphQL como REST con un solo endpoint; el poder está en el schema, no en el URL
- No versionar el schema; la evolución requiere disciplina, no magia
Preguntas Frecuentes
¿Puedo usar GraphQL y REST juntos?
Sí, y muchas organizaciones lo hacen. Un patrón común es mantener REST para servicios internos simples y agregar una capa de agregación GraphQL para clientes externos. Otro patrón es exponer REST para operaciones CRUD simples y GraphQL para queries complejas. La federación GraphQL permite combinar múltiples servicios REST bajo un solo schema sin modificar los servicios subyacentes.
¿Cómo migro de REST a GraphQL sin reescribir todo?
Agrega un servidor GraphQL que actúe como proxy/resolver sobre tus endpoints REST existentes. Cada resolver GraphQL llama a un endpoint REST. Esto te da beneficios inmediatos (queries flexibles, un solo endpoint) sin reescribir servicios. Luego, migra gradualmente los resolvers más críticos a queries directas a la base de datos o a servicios internos.
¿GraphQL es más lento que REST?
Depende. Para queries simples de un solo recurso, REST es más rápido debido al caching HTTP y menor overhead. Para queries complejas con múltiples relaciones, GraphQL puede ser más rápido porque hace un solo request en lugar de múltiples requests REST. El rendimiento de GraphQL depende casi enteramente de la calidad de tus resolvers y si usas DataLoader.
Recursos Relacionados
REST API Design Guide
A comprehensive guide to designing clean, scalable, and maintainable REST APIs.
GuideAPI Gateway Design — Resilience, Routing, and Security
A practical guide to designing API gateways: routing patterns, rate limiting, authentication, circuit breakers, and observability for resilient APIs.
DocAPI Performance Budget Template
A template for setting and tracking API latency and throughput performance budgets.
GuideSystem Design Interview Guide — Key Concepts
A practical guide to system design interviews: scalability, databases, caching, load balancing, microservices, and how to structure your answer.
DocAPI Lifecycle Management Template
A checklist template for API deprecation, versioning, and sunset procedures.