El error de plantear "REST o GraphQL" como una guerra de bandos
Cada vez que alguien me pregunta «¿deberíamos pasarnos a GraphQL?», la respuesta corta es: depende de quién va a consumir la API y de qué problema te está doliendo hoy. REST y GraphQL no son dos versiones de lo mismo — resuelven problemas distintos, y en mi día a día con Magento y Laravel acabo usando los dos, a veces en el mismo proyecto.
El error que veo más a menudo es elegir GraphQL porque «es lo moderno» para una API que solo va a consumir un backoffice interno, o quedarse con REST por costumbre cuando el frontend necesita combinar datos de cinco endpoints distintos en cada pantalla.
En resumen, esto es lo que miro antes de decidir:
- ¿Quién consume la API? Un frontend propio con vistas variables → GraphQL tiene sentido. Integraciones con terceros o partners → REST es más fácil de documentar y depurar.
- ¿Necesitas caching HTTP estándar (CDN, Varnish)? → REST. GraphQL complica el caching porque casi todo va por POST a un único endpoint.
- ¿El cliente necesita combinar datos de varias entidades relacionadas en una sola pantalla? → GraphQL evita el over-fetching y el «endpoint a medida para cada vista».
- ¿Es Magento headless (PWA Studio)? → GraphQL ya viene decidido por la arquitectura.
- ¿El equipo ya tiene experiencia con GraphQL? → Si no, la curva de aprendizaje (resolvers, N+1, caching) tiene un coste real.
El resto del artículo explica el porqué de cada una, con ejemplos de Magento y Laravel.
Qué problema resuelve cada uno
REST modela la API como recursos (/orders, /orders/123, /customers/45) y cada endpoint devuelve una forma fija de datos. Es predecible, se beneficia de todo el ecosistema HTTP (caching, códigos de estado, herramientas de depuración) y cualquier desarrollador lo entiende sin documentación adicional.
GraphQL expone un único endpoint y deja que el cliente describa exactamente qué datos necesita, incluyendo relaciones anidadas, en una sola petición. Resuelve dos problemas muy concretos de REST: el over-fetching (el endpoint devuelve más campos de los que la vista necesita) y el under-fetching (la vista necesita encadenar varias llamadas para montar la pantalla).
Peculiaridades de REST
- Versionado. Cambiar la forma de un recurso obliga a versionar (
/v2/orders) o a aceptar que rompes a los clientes existentes. - Caching HTTP de serie.
ETag,Cache-Controly un CDN o Varnish delante funcionan sin esfuerzo adicional — fundamental en eCommerce, donde el catálogo se sirve a miles de visitas anónimas. - Riesgo de under-fetching. Una pantalla de «detalle de pedido con cliente y líneas» puede acabar siendo 3 peticiones si no diseñas bien los endpoints.
En Laravel, esto se traduce en un API Resource por recurso:
// app/Http/Resources/OrderResource.php
class OrderResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'reference' => $this->reference,
'total' => $this->total,
'status' => $this->status,
'customer' => new CustomerResource($this->whenLoaded('customer')),
'lines' => OrderLineResource::collection($this->whenLoaded('lines')),
];
}
}
// routes/api.php
Route::apiResource('orders', OrderController::class);
Peculiaridades de GraphQL
- Un único endpoint. Toda la API vive en
/graphql, lo que simplifica el cliente pero complica el caching HTTP tradicional, ya que casi todo va por POST. - El problema N+1 se traslada al resolver. Si
Order.customeryOrder.linesse resuelven cada uno con su propia query, pedir 50 pedidos con cliente y líneas dispara cientos de queries si no usas batching (DataLoader o similar). - La seguridad cambia de forma. Ya no controlas el acceso por endpoint, sino por complejidad de query — alguien puede pedir relaciones anidadas varios niveles y tumbar la base de datos con una sola petición «válida».
En Laravel, la alternativa habitual es Lighthouse, que define el esquema de forma declarativa:
type Order {
id: ID!
reference: String!
total: Float!
status: OrderStatus!
customer: Customer! @belongsTo
lines: [OrderLine!]! @hasMany
}
type Query {
order(id: ID! @eq): Order @find
orders: [Order!]! @paginate
}
El caso Magento: la arquitectura ya decide buena parte
Magento 2 trae ambas APIs de serie, y en la práctica cada una tiene un rol distinto:
- REST está pensada para integraciones backend: ERPs, PIMs, sincronización de pedidos y stock. Es la API que ya conocen los sistemas con los que vas a integrar, con autenticación por token de integración bien documentada.
- GraphQL nació para PWA Studio y los storefronts headless: catálogo, carrito y checkout necesitan combinar producto, precio, stock y atributos configurables en una sola pantalla, y GraphQL evita encadenar media docena de llamadas REST por cada página de producto.
{
products(filter: { sku: { eq: "24-MB01" } }) {
items {
name
sku
price_range {
minimum_price {
regular_price { value currency }
}
}
... on ConfigurableProduct {
configurable_options { attribute_code values { label } }
}
}
}
}
Si el proyecto es un storefront headless sobre Magento, GraphQL no es una opción que evalúas: viene dado. Si lo que montas es una integración con el ERP del cliente, REST sigue siendo el camino más simple y mejor soportado por el ecosistema.
El caso Laravel: API Resources vs Lighthouse
En un proyecto Laravel «normal» (panel de administración, backoffice, integración puntual), REST con API Resources es la opción por defecto: no añade dependencias, cualquiera en el equipo lo entiende, y el caching HTTP funciona sin configuración extra.
Lighthouse empieza a tener sentido cuando el consumidor es un frontend propio con vistas muy variables — por ejemplo, una app móvil donde la pantalla de listado necesita 5 campos por pedido pero la de detalle necesita 30, y no quieres mantener dos endpoints o un parámetro ?fields= casero que reinventa GraphQL a medias.
Lo que casi nunca compensa: montar GraphQL para una API que solo va a consumir tu propio backoffice interno, o para una integración con un tercero que espera REST. Ahí GraphQL añade una capa de complejidad — resolvers, N+1, autenticación de queries — sin que nadie se beneficie de su flexibilidad.
Las preguntas que me hago antes de elegir
- ¿Quién consume la API: un frontend propio o un tercero? → Frontend propio con vistas variables: GraphQL. Terceros e integraciones: REST.
- ¿Necesitas que un CDN o Varnish cacheen las respuestas? → REST. Cachear GraphQL bien requiere persisted queries y trabajo adicional.
- ¿Es Magento headless (PWA Studio) o un tema/checkout estándar? → Headless: GraphQL ya viene decidido. Estándar: REST para lo que necesites tocar desde fuera.
- ¿El equipo tiene experiencia con resolvers, N+1 y DataLoader? → Si no, cuenta ese aprendizaje como parte del coste del proyecto.
- ¿La API va a crecer con muchas vistas distintas del mismo dato? → Cuantas más combinaciones de campos necesites, más pesa GraphQL en la balanza.
Conclusión
REST y GraphQL no compiten por el mismo puesto. REST sigue siendo la opción más simple, más cacheable y más fácil de integrar con terceros — y por eso es mi punto de partida por defecto. GraphQL gana cuando el consumidor es un frontend propio con necesidades de datos muy variables, o cuando la plataforma, como Magento headless, ya lo trae integrado.
Si tu proyecto tiene Magento y Laravel en la misma conversación y no tienes claro por dónde tirar para la API, cuéntame en qué consiste.