🔌 Backend & APIs 🧱 Full Stack

GraphQL vs REST vs tRPC: cuándo usar cada uno

Ni REST está obsoleto ni GraphQL es la solución universal ni tRPC vale para cualquier equipo. Comparativa honesta de los tres estilos con sus trade-offs reales.

📅 5 de abril de 2026 ⏱️ 6 min de lectura ✍️ Equipo ProgramacionWebs

La pregunta “¿REST, GraphQL o tRPC?” se responde mal casi siempre porque se plantea como una elección ideológica en vez de como lo que realmente es: una decisión de arquitectura condicionada por quién consume tu API, con qué lenguajes, y cuánto control tienes sobre ambos lados de la conexión. Los tres estilos siguen vivos en 2026 porque cada uno resuelve bien un problema que los otros dos resuelven peor.

Si aún no tienes claras las bases del diseño REST, conviene leer antes Diseño de APIs REST en 2026: buenas prácticas que de verdad importan; este artículo asume ese contexto y se centra en cuándo conviene apartarse de REST.

REST: el estándar por defecto, con sus límites conocidos

REST sigue siendo la opción correcta por defecto para la mayoría de APIs, y no por inercia: es stateless, se apoya en la semántica ya existente de HTTP (métodos, códigos de estado, cacheo), es fácil de depurar con herramientas genéricas (curl, el propio navegador, cualquier cliente HTTP) y no exige que el consumidor instale nada especial para hablar con ella.

Sus dos problemas de siempre siguen siendo reales:

  • Overfetching: un endpoint /users/42 que devuelve 30 campos cuando la pantalla solo necesita el nombre y el avatar. El cliente paga el coste de transferir y parsear datos que no usa.
  • Underfetching: para pintar una pantalla necesitas datos de tres recursos distintos (/users/42, /users/42/orders, /orders/91/items), y eso son tres viajes de ida y vuelta, o un endpoint a medida que agrega los tres.

Ninguno de los dos es un defecto fatal. Se pueden mitigar con query params (?fields=name,avatar), con endpoints de agregación pensados para una pantalla concreta, o simplemente aceptando el coste porque el payload es pequeño. El problema aparece cuando una API sirve a muchos clientes distintos (web, móvil, terceros) con necesidades de datos muy diferentes entre sí: ahí el catálogo de endpoints a medida empieza a crecer sin control.

GraphQL: el cliente pide exactamente lo que necesita, a cambio de complejidad de servidor

GraphQL resuelve el overfetching y el underfetching por diseño: el cliente envía una query que describe la forma exacta de los datos que necesita, y el servidor la resuelve en una sola petición, sin importar cuántos recursos subyacentes tenga que combinar.

query PedidoConItems {
  order(id: "91") {
    total
    customer {
      name
    }
    items {
      productName
      quantity
    }
  }
}

Esa flexibilidad tiene un coste que suele subestimarse al evaluar GraphQL solo por su demo inicial:

  • Cacheo HTTP se pierde de forma nativa. REST se apoya en que cada recurso tiene su propia URL cacheable por CDNs y navegadores sin configuración extra. GraphQL expone típicamente un único endpoint (POST /graphql), así que el cacheo HTTP estándar deja de funcionar y hay que resolverlo a otro nivel: cacheo en el cliente (Apollo Client, Relay), persisted queries, o una capa de cacheo específica para GraphQL.
  • El coste se traslada al servidor. Cada query arbitraria que un cliente puede construir es, en el fondo, una consulta que el servidor tiene que resolver combinando fuentes de datos. Sin límites de profundidad y complejidad de query, un cliente (malicioso o simplemente descuidado) puede construir una consulta anidada que tumbe el servidor. Esto exige inversión adicional en límites de complejidad, rate limiting específico por coste de query y buen diseño de resolvers para evitar el clásico problema N+1.
  • Introducir GraphQL en un equipo no es gratis. Requiere un esquema bien diseñado desde el principio (los cambios de esquema son más delicados de versionar que añadir un campo a un JSON de REST), resolvers, y curva de aprendizaje tanto en el backend como en el frontend para consumidores nuevos en el ecosistema.

tRPC: tipado end-to-end sin generar código, si vives en un monorepo TypeScript

tRPC parte de una premisa distinta a las dos anteriores: si el cliente y el servidor están escritos en TypeScript y viven en el mismo repositorio (o al menos comparten un paquete de tipos), no hace falta un lenguaje de esquema intermedio ni generación de código. Los tipos del servidor se infieren directamente en el cliente porque ambos comparten el mismo compilador de TypeScript.

// server/routers/orders.ts
export const ordersRouter = router({
  getById: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => db.orders.findById(input.id)),
});

// client.ts — sin generación de código, tipos inferidos en el momento
const order = await trpc.orders.getById.query({ id: '91' });
// order.total, order.items... con autocompletado real

Cuando cambias el tipo de retorno de getById en el servidor, el cliente ve el error de tipos al instante, sin ejecutar ningún paso de codegen intermedio. Esa retroalimentación inmediata es la razón por la que equipos que ya trabajan en TypeScript de punta a punta —algo cada vez más habitual en arquitecturas full-stack modernas— lo adoptan para su API interna.

El límite de tRPC es también su premisa: en el momento en que un cliente necesita consumir la API en otro lenguaje —una app móvil nativa en Kotlin o Swift, un partner que integra en Python—, tRPC deja de ser una opción para esa superficie, porque su propuesta de valor depende por completo de compartir el sistema de tipos de TypeScript. Para ese escenario, la API pública sigue necesitando REST (o GraphQL) con un contrato explícito e independiente del lenguaje.

graph TD
A["¿Quién consume la API?"] --> B{Solo tu propio frontend TypeScript, mismo monorepo}
B -->|Sí| C[tRPC: tipado end-to-end sin codegen]
B -->|No| D{Clientes con necesidades de datos muy distintas entre sí}
D -->|Sí| E[GraphQL: el cliente pide la forma exacta]
D -->|No, CRUD razonablemente uniforme| F[REST: simple, cacheable, depurable]

Combinar los tres no es una contradicción

La idea de que hay que “elegir un ganador” para toda la organización no resiste el contacto con sistemas reales de cierto tamaño. Es habitual y razonable que una misma empresa use REST para su API pública orientada a terceros (porque necesita ser consumida desde cualquier lenguaje y beneficiarse del cacheo HTTP estándar), tRPC para la comunicación interna entre su frontend y su backend en el mismo monorepo (por la velocidad de desarrollo y la seguridad de tipos), y GraphQL en una capa de agregación cuando varios servicios internos necesitan exponerse de forma unificada a distintos clientes.

La pregunta que de verdad hay que responder antes de elegir no es “¿cuál es mejor?”, sino tres más concretas: ¿en qué lenguajes vive el consumidor de esta API?, ¿necesito que el cacheo HTTP estándar funcione sin esfuerzo extra?, y ¿cuánta variedad real hay entre las necesidades de datos de los distintos clientes? Las respuestas a esas tres preguntas señalan el estilo correcto con bastante más fiabilidad que cualquier comparativa de benchmarks aislados.

Compartir