GraphQL

Recursos


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 @deprecated para 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 sub­esquemas.  
- 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