GraphQL
- Backend
- api
- JSON
- websockets
- web services
- node.js
- shopify
Recursos
-
[Introduction to GraphQL GraphQL](https://graphql.org/learn/) - GitHub - graphql/graphql.github.io
-
[Running an Express GraphQL Server GraphQL](https://graphql.org/graphql-js/running-an-express-graphql-server/) - GitHub GraphQL API documentation - GitHub Docs
- GraphQL - Wikipedia-GraphQL
- ¿Qué es GraphQL IBM-graphql
- How to GraphQL - The Fullstack Tutorial for GraphQL
Conceptos fundamentales
¿Qué es GraphQL?
GraphQL es un lenguaje de consulta y un runtime para APIs que permite a los clientes definir exactamente qué datos necesitan. Fue creado por Facebook para resolver problemas comunes en APIs REST, como el overfetching (exceso de datos) y underfetching (falta de datos).
A diferencia de REST, no se basa en múltiples endpoints, sino en una única URL que interpreta consultas estructuradas.
Estructura básica
- Schema: define los tipos de datos, sus relaciones y operaciones posibles.
- Query: solicita datos (similar a GET en REST).
- Mutation: modifica datos (similar a POST, PUT, DELETE).
- Subscription: gestiona flujos de datos en tiempo real usando websockets.
Componentes del Schema
Definición de Schema
El schema describe las entidades que pueden consultarse y cómo se relacionan entre sí.
Ejemplo básico:
type User {
id: ID!
name: String!
email: String!
}
type Query {
users: [User]
user(id: ID!): User
}
`
Tipos de datos
- Escalares:
Int,Float,String,Boolean,ID - Objetos: estructuras definidas por el usuario
- Enums: listas de valores permitidos
- Interfaces y Unions: definen relaciones polimórficas entre tipos
- Directivas: como
@deprecatedpara marcar campos obsoletos
Operaciones principales
Querys
Permiten consultar datos específicos, incluso de entidades relacionadas, evitando múltiples llamadas.
query {
user(id: "1") {
name
email
posts {
title
content
}
}
}
Mutations
Usadas para crear, modificar o eliminar datos.
mutation {
createUser(name: "Eduardo", email: "edu@example.com") {
id
name
}
}
Subscriptions
Permiten actualizaciones en tiempo real usando websockets.
subscription {
userCreated {
id
name
}
}
Comparativa con REST
| Característica | REST | GraphQL |
|---|---|---|
| Endpoints | Múltiples | Uno solo |
| Peticiones | GET, POST, PUT, DELETE | Query, Mutation, Subscription |
| Estructura de respuesta | Fija | Flexible (definida por el cliente) |
| Problemas comunes | Overfetching, Underfetching | Minimiza ambos |
| Comunicación en tiempo real | Limitada | Integrada (Subscriptions) |
Problemas comunes y optimización
Underfetching y Overfetching
- Underfetching: la API devuelve menos datos de los necesarios → múltiples llamadas.
- Overfetching: la API devuelve más datos de los necesarios → uso ineficiente de recursos.
Solución: GraphQL permite consultas precisas que reducen ambos casos.
Consultas costosas
El servidor debe validar y limitar la complejidad de las consultas para evitar abusos o degradación de rendimiento.
Optimización
- Implementar control de profundidad de queries.
- Usar caching selectivo en resolvers.
- Monitorizar consultas más frecuentes.
- Agrupar resolvers con dataloader para evitar N+1 queries.
Integración y ecosistema
En node.js
GraphQL se implementa comúnmente con Express o Apollo Server.
Ejemplo básico usando express-graphql:
import express from "express";
import { graphqlHTTP } from "express-graphql";
import { buildSchema } from "graphql";
const schema = buildSchema(`
type Query {
hello: String
}
`);
const root = { hello: () => "Hello world!" };
const app = express();
app.use("/graphql", graphqlHTTP({ schema, rootValue: root, graphiql: true }));
app.listen(4000);
console.log("Servidor GraphQL en http://localhost:4000/graphql");
En ecosistemas modernos
- shopify utiliza GraphQL extensivamente para su API Storefront.
- GitHub ofrece su API principal en GraphQL para obtener datos de repositorios y usuarios de manera optimizada.
- Apollo Federation permite unir múltiples servicios GraphQL en un solo gateway.
Buenas prácticas
- Diseñar schemas simples y predecibles.
- Documentar queries y mutations.
- Evitar anidamientos excesivos.
- Controlar la autenticación y autorización a nivel de campo.
- Implementar paginación y filtros en las queries.
- Validar la complejidad de las consultas en el backend.
Tareas pendientes
- Probar APIs de ejemplo (GraphQL Playground o Apollo Sandbox)
- Integración con Git y despliegue continuo
- Añadir ejemplos de autenticación con tokens y contextos
GraphQL – Temas avanzados
Fragmentos reutilizables, alias y argumentos
- Fragments: Permiten definir porciones de consulta reutilizables para evitar duplicación. :contentReference[oaicite:1]{index=1}
fragment addressFields on User {
name
street
city
zipcode
}
query {
users {
...addressFields
}
}
`
- Alias: Permiten nombrar resultados de campos con argumentos, cuando usas el mismo campo varias veces. Ejemplo:
{
first: user(id:"1") { name }
second: user(id:"2") { name }
}
```
:contentReference[oaicite:2]{index=2}
- Argumentos de campo: Los campos pueden recibir argumentos, incluso con valores por defecto, lo cual da más expresividad al esquema. ([howtographql.com][1])
Tipos avanzados de SDL (Schema Definition Language)
- Puedes definir escalares personalizados,
enum,interface,union. ([howtographql.com][1]) -
Inline fragments: Se usan para consultas condicionales en tipos union o interface. ([Shopify][2])
``` graphql union SearchResult = User | Product query { search(text:”foo”) { … on User { id name } … on Product { id title price } } }
## Persisted queries y batching
* **Persisted queries**: Consultas pre-registradas en el servidor para reducir coste de parsing, mejorar caché, etc. ([Experience League][3])
* **Batched queries**: Envío de múltiples operaciones (queries/mutations) en una sola petición, con alias, aunque la complejidad se suma. ([Shopify][2])
## Arquitectura, escalabilidad y federación
* **Federation / composición de servicios**: Permite que múltiples microservicios GraphQL participen en un gateway unificado (por ejemplo Apollo Federation). ([blog.bytebytego.com][4])
* **Escalabilidad en producción**: Caching, balanceo, monitorización, limitación de complejidad de queries. ([Medium][5])
## Seguridad, validación y rendimiento
* Validación de entradas, control de profundidad, coste de consulta para evitar abusos. ([Medium][6])
* Seguridad específica para GraphQL: ataques de inyección, denegación de servicio por consultas complejas, análisis de dependencias entre consultas. ([arXiv][7])
* Optimización de consultas: paginación, límite de profundidad, data-loader para evitar N+1.
## Herramientas y generación de código
* Generación automática de tipos y consultas para lenguajes fuertemente tipados (TypeScript, etc) que se alinean con el esquema GraphQL. ([Medium][5])
* Depuración, tracing y monitorización de ejecución de consultas (por ejemplo con Apollo Studio o similares).
## Integración con microservicios y bases de datos gráficas
* Uso de GraphQL sobre arquitecturas de microservicios: cada servicio expone su parte del esquema, se compone todo. ([blog.bytebytego.com][4])
* Ejecución de GraphQL sobre grafos RDF o bases de datos de grafos, con algoritmos de join multi-way optimizados. ([arXiv][8])
## Buenas prácticas avanzadas
* Versionado cero (evitar breaking changes añadiendo campos y usando directivas como `@deprecated`).
* Diseñar el esquema pensando en el cliente: anticipar qué datos necesitará y cómo evolucionará.
* Usar métricas de complejidad de consulta para rechazar o limitar queries que podrían degradar el rendimiento.
* Instrumentar logs y alertas específicas para GraphQL (autenticación, autorización, performance).
* Mantener la separación de responsabilidades entre schema (contrato) y resolvers (implementación).
## Lista de verificación para producción
- Definir estrategias de caché para respuestas, campos y nivel de consulta.
- Implementar limitación de profundidad y coste en consultas (maxDepth, maxComplexity).
- Validar entradas (input types) y sanitizar argumentos antes de resolver.
- Auditar operaciones de suscripción: autenticación, escala, desconexiones.
- Monitorizar latencias de resolvers, tiempos de respuesta globales, tasa de errores.
- Planificar fallback o degradación de servicio ante cargas elevadas.
- Registrar y alertar operaciones sospechosas en esquemas GraphQL dinámicos.
## fuentes
- [1]: https://www.howtographql.com/advanced/2-more-graphql-concepts/?utm_source=chatgpt.com "Advanced GraphQL Language Concepts Tutorial"
- [2]: https://shopify.dev/docs/apps/build/graphql/basics/advanced?utm_source=chatgpt.com "Advanced concepts"
- [3]: https://experienceleague.adobe.com/en/docs/experience-manager-learn/getting-started-with-aem-headless/graphql/advanced-tutorial/overview?utm_source=chatgpt.com "Advanced Concepts of AEM Headless - GraphQL"
- [4]: https://blog.bytebytego.com/p/graphql-101-api-approach-beyond-rest?utm_source=chatgpt.com "GraphQL 101: API Approach Beyond REST"
- [5]: https://mobilelive.medium.com/graphql-advanced-concepts-a-comprehensive-guide-3479355d4709?utm_source=chatgpt.com "GraphQL Advanced Concepts: A Comprehensive Guide"
- [6]: https://ahmedrebai.medium.com/graphql-advanced-concepts-f7557b6132f4?utm_source=chatgpt.com "GraphQL advanced concepts . (Feb Blog) | by Rebai Ahmed"
- [7]: https://arxiv.org/abs/2504.13358?utm_source=chatgpt.com "GraphQLer: Enhancing GraphQL Security with Context-Aware API Testing"
- [8]: https://arxiv.org/abs/2409.12646?utm_source=chatgpt.com "Native Execution of GraphQL Queries over RDF Graphs Using Multi-way Joins"
# GraphQL – Temas adicionales
## Schema stitching vs federación profunda
- A diferencia de la federación pura, el llamado *schema stitching* permite combinar distintos esquemas GraphQL en uno sólo sin usar necesariamente el estándar de Apollo Federation.
- Esto puede usarse para integrar servicios REST, GraphQL externos o bases de datos distintas bajo un único esquema. :contentReference[oaicite:2]{index=2}
- Ventajas: menor acoplamiento entre equipos, migraciones más suaves, menor dependencia de un único proveedor.
- Desventajas: puede requerir configuración compleja y los planes de ejecución pueden volverse más opacos.
## Transporte y protocolo para suscripciones federadas
- En escenarios federados, gestionar suscripciones (real-time) es especialmente desafiante: múltiples servicios, múltiples conexiones, sincronización. :contentReference[oaicite:3]{index=3}
- Ejemplo avanzado: en Apollo GraphOS se introduce un nuevo protocolo `multipart-subscriptions` para cliente→gateway y deduplicación de conexiones gateway→subgraph. :contentReference[oaicite:5]{index=5}
- Tips para diseñar: usar WebSocket o HTTP2 con multiplexación; deduplicar conexiones cuando muchos clientes usan la misma suscripción; minimizar latencia.
## Validación, seguridad y testing automatizado
- Vulnerabilidades específicas de GraphQL: inyección de consultas, mediación de autorización en campos, ataques de denegación de servicio via queries altamente complejas. :contentReference[oaicite:6]{index=6}
- Buenas prácticas:
- Definir qué campos pueden consultarse y con qué argumentos.
- Limitación de profundidad (`maxDepth`), complejidad (`maxComplexity`) de la query.
- Testing automatizado que entienda el grafo de dependencias entre queries/mutations para detectar bypass de autorización.
- Monitorización de patrones de uso sospechosos.
## Caching, persistencia y control de respuestas
- Cache a nivel de campo / tipo: en federación, puedes indicar mediante directivas como `@cacheControl(maxAge: Int, scope: PUBLIC|PRIVATE)` qué nivel de caché debe aplicar cada campo. :contentReference[oaicite:7]{index=7}
- Persisted queries (consultas pre-registradas) que permiten reducir el parsing y mejorar la caché de consultas repetitivas.
- Cache del cliente, cache del servidor: definir políticas coherentes para invalidación post-mutación, uso de `ResponseCachePlugin`, etc. :contentReference[oaicite:8]{index=8}
## Migraciones de API, versionado y evolución del esquema
- Con GraphQL la versión tradicional de "v1, v2, v3" se reemplaza por evolución continua del esquema:
- Agregar campos nuevos en tipos existentes (sin romper compatibilidad).
- Marcar campos obsoletos con `@deprecated(reason: "...")`.
- Evitar eliminar campos que los clientes aún usan.
- En federación, esto es aún más crítico porque múltiples equipos pueden exponer subesquemas.
- Plan de evolución:
- Documentar uso de cada campo.
- Comunicar deprecaciones a los equipos clientes.
- Usar herramientas de generación de tipos (TypeScript, etc) para que los cambios se propaguen en tiempo de compilación.
## Logging, métricas y observabilidad en producción
- Asegúrate de que tu infraestructura GraphQL cuente con:
- Tiempos de ejecución de resolvers, latencia por operación.
- Nº de nodos devueltos, profundidad de consulta, tamaño del payload.
- Métricas de uso de suscripciones: conexiones activas, mensajes por segundo.
- Alertas para esquemas con muchas peticiones, errores frecuentes, uso de campos `@deprecated`.
- Estas métricas permiten anticipar cuellos de botella, abusos o degradación del servicio.
## Integración con otras tecnologías y fuentes de datos
- Usar GraphQL como “fachada” sobre diferentes backends: REST, bases de datos NoSQL, microservicios, servicios event-driven.
- En federación, cada subgrafo puede estar implementado con distinto stack tecnológico, y tú los unificas bajo un único gateway.
- La estrategia de migración “por piezas” permite que un tipo o campo migre a servicio nuevo sin interrupciones (como lo describe el caso de WunderGraph). :contentReference[oaicite:10]{index=10}
## Estrategia de errores y fallas degradadas
- En consultas federadas o complejas, pueden fallar subresolvers individuales. La estrategia debe contemplar:
- Devolver datos parciales útiles en lugar de fallo general (“best-effort”).
- Utilizar directivas como `@defer` y `@stream` para ofrecer partes de la respuesta que lleguen antes.
- Diseñar mecanismos de fallback cuando un subgrafo no está disponible: cache reciente, valores predeterminados, lógica de retry.
# GraphQL – Implementación en producción
## Estructura general de proyecto
Un entorno de producción en GraphQL suele organizarse en capas bien definidas:
1. **Schema y resolvers** – definición del contrato y lógica de obtención de datos.
2. **Módulo de seguridad** – autenticación, autorización, validación.
3. **Optimización de rendimiento** – caché, batching, límites de complejidad.
4. **Observabilidad y métricas** – logging, tracing, monitorización.
5. **Evolución del esquema** – gestión de cambios, deprecaciones, comunicación con clientes.
6. **Gateway / federación** – integración de múltiples servicios o fuentes de datos.
---
## Configuración base con Apollo Server
js // server.js import { ApolloServer } from ‘@apollo/server’; import { startStandaloneServer } from ‘@apollo/server/standalone’; import { typeDefs } from ‘./schema.js’; import { resolvers } from ‘./resolvers.js’; import depthLimit from ‘graphql-depth-limit’; import { createComplexityLimitRule } from ‘graphql-validation-complexity’;
const server = new ApolloServer({ typeDefs, resolvers, validationRules: [ depthLimit(10), createComplexityLimitRule(2000) ], introspection: process.env.NODE_ENV !== ‘production’ });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, context: async ({ req }) => ({ token: req.headers.authorization || ‘’ }) });
console.log(🚀 Servidor listo en ${url});
---
## Validación y seguridad
### Autenticación y autorización
* Implementar un contexto compartido que propague el usuario autenticado.
* A nivel de resolver, validar los permisos con el contexto.
* Evitar exponer información sensible en errores.
js // resolvers.js export const resolvers = { Query: { secretData: (_, __, { user }) => { if (!user || !user.isAdmin) throw new Error(“Acceso denegado”); return getSensitiveInfo(); } } };
### Protección contra abusos
* Limitar profundidad y complejidad de consultas.
* Desactivar introspección en producción.
* Validar inputs y sanitizar argumentos.
---
## Caching y optimización
### Cache por campo o tipo
graphql type Product @cacheControl(maxAge: 60, scope: PUBLIC) { id: ID! name: String! price: Float }
### Uso de DataLoader
Evita el problema N+1 cargando datos en batch.
js import DataLoader from ‘dataloader’; const userLoader = new DataLoader(async (ids) => { const users = await db.users.find({ _id: { $in: ids } }); return ids.map(id => users.find(u => u.id === id)); });
### Persisted queries
Permiten reducir el coste de parsing y prevenir inyección de consultas.
* Pre-registrar queries hashadas en el servidor.
* El cliente envía solo el hash de la consulta.
* Ideal para APIs públicas o móviles.
---
## Monitorización y métricas
### Ejemplo de integración con Apollo Studio
js new ApolloServer({ typeDefs, resolvers, plugins: [ require(‘apollo-server-core’).ApolloServerPluginUsageReporting({ sendVariableValues: { all: true }, sendHeaders: { all: true } }) ] });
### Métricas recomendadas
* Tiempo promedio por resolver.
* Nº de queries por tipo.
* Profundidad media y complejidad de consulta.
* Ratio de errores.
* Campos más utilizados.
---
## Estrategia de evolución del esquema
### Deprecación controlada
graphql type User { email: String @deprecated(reason: “Use contact.email en su lugar”) contact: Contact }
### Auditoría de uso
* Analizar logs de queries para detectar campos obsoletos.
* Comunicar con los clientes las futuras eliminaciones.
* Mantener un changelog de schema.
---
## Integración con microservicios (Federation)
js // gateway.js import { ApolloGateway } from ‘@apollo/gateway’; import { ApolloServer } from ‘@apollo/server’;
const gateway = new ApolloGateway({ serviceList: [ { name: ‘users’, url: ‘http://localhost:4001/graphql’ }, { name: ‘orders’, url: ‘http://localhost:4002/graphql’ } ] });
const server = new ApolloServer({ gateway, subscriptions: false });
Ventajas:
* Escalado independiente por dominio.
* Equipos autónomos.
* Evolución modular del esquema.
---
## Estrategia de errores y fallbacks
### Devolución parcial con `@defer` y `@stream`
Permiten entregar datos parciales antes de completar toda la query.
graphql query { products { id name reviews @stream(initialCount: 2) { content } } }
### Respuesta parcial ante fallos
En resolvers federados, devolver datos parciales útiles:
js try { return await fetchOrder(id); } catch (err) { return { id, status: “UNKNOWN”, error: err.message }; }
---
## Plantillas útiles para producción
### .env
NODE_ENV=production PORT=4000 GRAPHQL_DEPTH_LIMIT=10 GRAPHQL_COMPLEXITY_LIMIT=2000 JWT_SECRET=yourSecretKey
### package.json (mínimo)
json { “scripts”: { “start”: “node server.js”, “dev”: “nodemon server.js” }, “dependencies”: { “@apollo/server”: “^4.0.0”, “graphql”: “^16.8.0”, “graphql-depth-limit”: “^1.1.0”, “graphql-validation-complexity”: “^0.4.2”, “dataloader”: “^2.1.0” } } ```
Checklist para despliegue
- Introspección desactivada en producción
- Límite de profundidad y complejidad configurado
- Cache por tipo y DataLoader activo
- Autenticación/Autorización verificadas
- Persisted queries habilitadas
- Logs y métricas en servidor centralizado
- Auditoría de campos obsoletos en curso
- Fallbacks implementados para errores federados
Recursos avanzados
¿Te gusta este contenido? Suscríbete vía RSS