¿Qué es GraphQL?
GraphQL es un lenguaje de consultas y un runtime para APIs. En lugar de exponer múltiples endpoints fijos, un servidor GraphQL expone un único schema tipado, y los clientes envían consultas que seleccionan exactamente los campos que necesitan. El servidor resuelve cada campo y devuelve una respuesta con la misma estructura que la solicitud.
Facebook lo creó en 2012 para resolver un problema en dispositivos móviles: los endpoints de REST devolvían demasiados o muy pocos datos, y obtener toda la información relacionada para una pantalla requería demasiados viajes de ida y vuelta (round trips). GraphQL solucionó ambos problemas permitiendo que el cliente describiera sus requisitos de datos en un solo documento.
El esquema
El esquema es el contrato. Define los tipos y las operaciones disponibles.
# schema.graphql
type User {
id: ID!
name: String!
email: String
posts(first: Int = 10): [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
author: User!
}
type Query {
user(id: ID!): User
posts(limit: Int): [Post!]!
}
type Mutation {
createPost(input: CreatePostInput!): Post!
}
El símbolo ! marca un campo como no nulo. Los tipos se componen en un grafo, razón por la cual una sola consulta puede navegar desde un usuario hacia sus posts y volver a los autores. El esquema es introspectable, por lo que las herramientas pueden generar documentación, tipos y autocompletado automáticamente.
Queries
Una query selecciona campos del tipo de query raíz, utilizando argumentos y variables.
# posts.graphql
query RecentPosts($limit: Int!) {
posts(limit: $limit) {
id
title
author {
name
}
}
}
La respuesta refleja exactamente la selección: sin campos adicionales ni omisiones. Las variables permiten que las queries sean reutilizables y dejan que el servidor valide los tipos de los argumentos. Los fragments extraen selecciones repetidas para su reutilización.
# fragment.graphql
fragment PostCard on Post {
id
title
author { name }
}
query Feed {
posts { ...PostCard }
}
Mutations y subscriptions
Una mutation modifica datos y devuelve una estructura definida, que a menudo incluye el objeto actualizado para que el cliente pueda actualizar su caché.
# create.graphql
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
}
}
Una subscription transmite actualizaciones en tiempo real a través de una conexión persistente, como mensajes nuevos o notificaciones en vivo. Utilízala cuando el servidor deba enviar datos al cliente; para actualizaciones ocasionales, el polling o un refetch es más sencillo.
Resolvers
Los resolvers son el punto de encuentro entre el esquema y tus datos. Cada campo tiene un resolver que devuelve su valor.
// resolvers.js
const resolvers = {
Query: {
user: (_parent, { id }, { db }) => db.users.findById(id),
posts: (_parent, { limit = 10 }, { db }) => db.posts.findMany({ limit }),
},
User: {
posts: (user, { first }, { db }) =>
db.posts.findMany({ where: { authorId: user.id }, limit: first }),
},
Mutation: {
createPost: (_parent, { input }, { db }) => db.posts.create(input),
},
};
El objeto context transporta recursos compartidos, como la base de datos y el usuario autenticado. Los resolvers deben ser pequeños, y las comprobaciones de autorización deben realizarse aquí o en la capa de servicio que invoquen — nunca en el cliente.
El problema N+1
La trampa de rendimiento más común en GraphQL son las consultas N+1. Si una consulta devuelve diez posts y el resolver del autor de cada post hace una petición a la base de datos, terminarás ejecutando once consultas.
DataLoader soluciona esto mediante el agrupamiento (batching) y el almacenamiento en caché de las llamadas dentro de una misma solicitud.
// loaders.js
import DataLoader from "dataloader";
function createUserLoader(db) {
return new DataLoader(async (ids) => {
const users = await db.users.findByIds(ids);
return ids.map((id) => users.find((u) => u.id === id));
});
}
Luego, el resolver del autor llama a loaders.user.load(post.authorId), y DataLoader agrupa todas las cargas del tick en una sola consulta. Esta es la optimización más importante que puedes implementar en un servidor GraphQL.
Clientes y almacenamiento en caché
Las librerías de cliente como Apollo Client y urql proporcionan almacenamiento en caché, normalización, gestión de estados de carga y error, e integración con frameworks de UI. Una caché normalizada almacena las entidades por tipo e id, de modo que una mutation que devuelve un post actualizado actualiza automáticamente cada query que haga referencia a él.
Debido a que GraphQL utiliza normalmente un único endpoint POST, el almacenamiento en caché de HTTP es menos efectivo que con REST. Los clientes compensan esto con cachés normalizadas, y los servidores utilizan persisted queries y almacenamiento en caché de respuestas. Diseña tus mutations para que devuelvan los objetos modificados y así la caché del cliente pueda mantenerse consistente.
¿GraphQL o REST?
GraphQL destaca cuando:
- Los clientes necesitan campos diferentes para distintas pantallas.
- Una pantalla requiere datos de muchos recursos relacionados.
- Buscas una API única, tipada y autodocumentada.
REST es más sencillo cuando:
- Los recursos se mapean limpiamente a endpoints.
- El almacenamiento en caché de HTTP y los códigos de estado son fundamentales.
- La API es pequeña y estable.
Muchos equipos utilizan ambos, empleando GraphQL para la API del producto y REST para webhooks, subida de archivos y endpoints públicos sencillos.
Mejores prácticas
- Diseña el esquema basándote en las necesidades del cliente, no en las tablas de la base de datos.
- Usa tipos no nulos de forma deliberada y gestiona las versiones del esquema mediante deprecaciones.
- Mantén los resolvers ligeros y desplaza la lógica hacia los servicios.
- Soluciona el problema N+1 con DataLoader desde el principio.
- Añade límites de profundidad y complejidad a las consultas para evitar abusos.
- Devuelve los objetos modificados en las mutations para mantener la consistencia de la caché.
- Usa persisted queries en producción para reducir el tamaño del payload.
Errores comunes
- Exponer el esquema de la base de datos directamente como el esquema de GraphQL.
- Ignorar el problema N+1 hasta que el rendimiento colapse.
- Solicitar todos los datos en cada consulta y perder el beneficio principal.
- Confiar en la autorización proporcionada por el cliente o en los permisos a nivel de campo.
- Construir consultas profundamente anidadas sin un límite de profundidad.
- Asumir que el almacenamiento en caché de HTTP funciona igual que con REST.
Próximos pasos
GraphQL es una forma potente de diseñar APIs centradas en el cliente. Impleméntalo sobre el protocolo HTTP, construye el servidor en Node.js y consúmelo desde React utilizando una librería de cliente. Después, modela una pantalla que conozcas bien como un esquema y una consulta, y observa qué tan naturalmente se mapean los requerimientos de datos.