Patrones de GraphQL Yoga
Guía rápida: Usa
createYoga+createSchemapara un servidor GraphQL compatible con la API Fetch que funciona en cualquier runtime de JS. Yoga v5 usa Envelop para la composición de plugins, SSE para suscripciones por defecto, enmascaramiento de errores integrado y CORS listo para usar. ImportaGraphQLErrordesdegraphql(nographql-yoga) para errores intencionales visibles al cliente. Prefiere plugins específicos de Yoga sobre los equivalentes de Envelop para optimizaciones a nivel HTTP.
<critical_requirements>
CRÍTICO: Antes de Usar Esta Skill
Todo el código debe seguir las convenciones del proyecto en CLAUDE.md (kebab-case, exportaciones nombradas, orden de importaciones,
import type, constantes nombradas)
(DEBES importar GraphQLError desde 'graphql', NO desde 'graphql-yoga' -- es la exportación estándar de graphql-js)
(DEBES preferir plugins específicos de Yoga sobre equivalentes de Envelop -- los plugins de Yoga operan en la capa HTTP y pueden saltar la ejecución completa de GraphQL para resultados cacheados/persistidos)
(DEBES usar createSchema desde 'graphql-yoga' para esquema primero -- pasar objetos typeDefs/resolvers directamente a createYoga no es compatible en v5)
(DEBES usar constantes nombradas para todos los valores numéricos -- timeouts, TTLs, números de puerto, límites)
</critical_requirements>
Auto-detección: GraphQL Yoga, graphql-yoga, createYoga, createSchema, createPubSub, Envelop, useResponseCache, useCSRFPrevention, usePersistedOperations, suscripciones GraphQL SSE, enmascaramiento de errores, maskedErrors, graphql-ws, hooks de plugins Yoga, onRequest, onParams
Cuándo usarlo:
- Construir un servidor GraphQL que necesite ejecutarse en Node.js, Bun, Deno o Cloudflare Workers
- APIs que requieran suscripciones mediante SSE (por defecto) o WebSocket
- Extender la ejecución de GraphQL con plugins Envelop (caché, autenticación, registro)
- Carga de archivos usando la especificación de Solicitud Multiparte de GraphQL
- APIs de producción que necesiten enmascaramiento de errores, CORS y protección CSRF
Cuándo NO usarlo:
- APIs solo REST sin necesidades de GraphQL
- CRUD simple donde los manejadores de ruta integrados de un framework son suficientes
- Cuando necesites un gateway federado (considera una solución de gateway dedicada)
Patrones clave cubiertos:
- Configuración del servidor con
createYogaycreateSchema(esquema primero) - Contexto con seguridad de tipos usando genéricos en
createYoga<ServerContext> - Sistema de plugins Envelop: hooks de ciclo de vida, plugins personalizados, plugins específicos de Yoga
- Suscripciones: SSE (por defecto), WebSocket mediante
graphql-ws, PubSub integrado - Enmascaramiento de errores y exposición intencional de
GraphQLError - Carga de archivos con el escalar
Filede WHATWG - Fortalecimiento de producción: CORS, prevención CSRF, GraphQL Armor, registro
- Despliegue en múltiples runtimes: Node.js, Bun, Deno, Cloudflare Workers
Recursos detallados:
- examples/core.md - Configuración del servidor, esquema, contexto, resolutores, despliegue en múltiples runtimes
- examples/plugins.md - Plugins Envelop, plugins personalizados, hooks de ciclo de vida
- examples/subscriptions.md - SSE, WebSocket, PubSub, filtrado
- examples/error-handling.md - Enmascaramiento de errores, GraphQLError, enmascaramiento personalizado
- examples/production.md - CORS, CSRF, caché de respuestas, operaciones persistidas, registro
- reference.md - Marcos de decisión, referencia de plugins, lista de verificación de producción
Filosofía
GraphQL Yoga es un servidor GraphQL con pilas incluidas y compatible con la API Fetch. Su núcleo está construido sobre la API Fetch de WHATWG (Request/Response), lo que lo hace agnóstico al runtime -- el mismo código de servidor se despliega en Node.js, Bun, Deno y runtimes periféricos. El sistema de plugins Envelop proporciona middleware componible tanto en la capa HTTP como en la de ejecución de GraphQL.
Enfoque de esquema: Yoga es agnóstico a la biblioteca de esquemas. Usa createSchema (SDL primero), Pothos (código primero) o graphql-js vainilla -- cualquier cosa que produzca un GraphQLSchema funciona.
Prioridad de plugins: Cuando existan tanto un plugin Envelop como un plugin específico de Yoga para la misma característica (caché, operaciones persistidas, defer/stream), elige siempre la variante de Yoga. Los plugins de Yoga se enganchan en la capa HTTP y pueden cortocircuitar antes de que comience la ejecución de GraphQL, omitiendo el análisis y la validación por completo para resultados cacheados o persistidos.
Filosofía de errores: Todos los errores inesperados se enmascaran por defecto en producción. Los errores intencionales se lanzan como GraphQLError del paquete graphql -- estos evitan el enmascaramiento y llegan a los clientes con su mensaje y extensiones intactos.
Patrones Principales
Patrón 1: Configuración del Servidor con createYoga
Crea una instancia de Yoga con createSchema para esquemas basados en SDL. La instancia de yoga ES un manejador de la API Fetch -- pásalo directamente al servidor HTTP de cualquier runtime.
import { createYoga, createSchema } from "graphql-yoga";
import { createServer } from "node:http";
const PORT = 4000;
const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */ `
type Query {
greeting(name: String!): String!
}
`,
resolvers: {
Query: {
greeting: (_, { name }) => `¡Hola, ${name}!`,
},
},
}),
});
const server = createServer(yoga);
server.listen(PORT, () => {
console.info(`Servidor corriendo en http://localhost:${PORT}/graphql`);
});
Por qué es bueno: createSchema envuelve makeExecutableSchema, la instancia de yoga es un manejador Fetch estándar, funciona en cualquier runtime
Ver examples/core.md para configuración completa, despliegue en múltiples runtimes y contexto con seguridad de tipos.
Patrón 2: Contexto con Seguridad de Tipos
Pasa un genérico a createYoga para tipificar el contexto específico del servidor. La fábrica de context recibe YogaInitialContext (que contiene request y params) y devuelve tu contexto personalizado.
import { createYoga, type YogaInitialContext } from "graphql-yoga";
interface ServerContext {
req: IncomingMessage;
res: ServerResponse;
}
const yoga = createYoga<ServerContext>({
schema,
context: async ({ request }: YogaInitialContext) => {
const token = request.headers.get("authorization");
return { user: token ? await verifyToken(token) : null };
},
});
Por qué es bueno: los tipos genéricos fluyen al parámetro de contexto del resolutor, request usa la API Fetch estándar (no req/res específicos del framework)
Ver examples/core.md para patrones de contexto completos.
Patrón 3: Sistema de Plugins Envelop
Los plugins se pasan en el array plugins. Usa plugins específicos de Yoga cuando estén disponibles -- operan en la capa HTTP y pueden saltar la ejecución completa de GraphQL.
import { createYoga } from "graphql-yoga";
import { useResponseCache } from "@graphql-yoga/plugin-response-cache";
const CACHE_TTL_MS = 2_000;
const yoga = createYoga({
schema,
plugins: [
useResponseCache({
session: () => null,
ttl: CACHE_TTL_MS,
}),
],
});
Por qué es bueno: la caché de respuestas de Yoga omite el análisis/validación para resultados cacheados (el equivalente de Envelop no puede), los plugins se componen sin conflictos
Ver examples/plugins.md para plugins personalizados, hooks de ciclo de vida y todos los plugins específicos de Yoga.
Patrón 4: Suscripciones con SSE (Por Defecto)
Yoga usa Server-Sent Events por defecto para suscripciones -- no se necesita configuración de WebSocket. Usa la sintaxis AsyncGenerator en los resolutores de suscripción.
const schema = createSchema({
typeDefs: /* GraphQL */ `
type Subscription {
countdown(from: Int!): Int!
}
`,
resolvers: {
Subscription: {
countdown: {
subscribe: async function* (_, { from }) {
for (let i = from; i >= 0; i--) {
await new Promise((resolve) => setTimeout(resolve, 1_000));
yield { countdown: i };
}
},
},
},
},
});
Por qué es bueno: no se necesita infraestructura WebSocket, funciona a través de proxies HTTP y balanceadores de carga, biblioteca graphql-sse para clientes
Ver examples/subscriptions.md para PubSub, configuración WebSocket y filtrado.
Patrón 5: Enmascaramiento de Errores y GraphQLError
Yoga enmascara todos los errores inesperados por defecto. Lanza GraphQLError (desde graphql) para errores intencionales visibles al cliente -- estos evitan el enmascaramiento.
import { GraphQLError } from "graphql";
const NOT_FOUND_CODE = "USER_NOT_FOUND";
throw new GraphQLError("Usuario no encontrado", {
extensions: { code: NOT_FOUND_CODE },
});
Por qué es bueno: los errores inesperados nunca filtran detalles internos (detalles de base de datos, trazas de pila), los errores intencionales pasan con mensaje + extensiones
Ver examples/error-handling.md para enmascaramiento personalizado, deshabilitar enmascaramiento y modo de desarrollo.
Patrón 6: Carga de Archivos
Yoga es compatible con la especificación de Solicitud Multiparte de GraphQL. Agrega un escalar File y recibe objetos File de WHATWG en los resolutores.
const schema = createSchema({
typeDefs: /* GraphQL */ `
scalar File
type Mutation {
uploadFile(file: File!): Boolean!
}
`,
resolvers: {
Mutation: {
uploadFile: async (_, { file }: { file: File }) => {
const content = await file.arrayBuffer();
// Procesar contenido del archivo
return true;
},
},
},
});
Por qué es bueno: usa la API File estándar de WHATWG (igual que el navegador), no se necesitan paquetes adicionales, deshabilita con multipart: false
Ver examples/core.md para patrones completos de carga de archivos.
<decision_framework>
Marco de Decisión
Enfoque de Esquema
¿Necesitas tipos generados automáticamente desde SDL?
+-- SÍ --> createSchema (esquema primero con typeDefs + resolvers)
+-- NO --> ¿Quieres inferencia completa de TypeScript en la definición del esquema?
+-- SÍ --> Biblioteca código primero (ej. Pothos) -- pasa el GraphQLSchema resultante a Yoga
+-- NO --> GraphQLSchema de graphql-js vainilla
Transporte de Suscripciones
¿Necesitas suscripciones?
+-- SÍ --> ¿Los clientes necesitan comunicación bidireccional?
| +-- SÍ --> WebSocket mediante graphql-ws (agrega paquetes ws + graphql-ws)
| +-- NO --> SSE (por defecto, configuración cero, funciona a través de proxies)
+-- NO --> No se necesita configuración de suscripción
Selección de Plugins
¿La característica está disponible como plugin específico de Yoga?
+-- SÍ --> Usa el plugin de Yoga (hooks a nivel HTTP, puede saltar la ejecución)
+-- NO --> Usa el plugin Envelop (hooks a nivel de ejecución de GraphQL)
Plugins Específicos de Yoga (Preferir Sobre Envelop)
| Plugin | Paquete | Por qué es específico de Yoga |
|---|---|---|
| Caché de Respuestas | @graphql-yoga/plugin-response-cache | Omite la ejecución para consultas cacheadas |
| Operaciones Persistidas | @graphql-yoga/plugin-persisted-operations | Rechaza operaciones desconocidas en la capa HTTP |
| Defer/Stream | @graphql-yoga/plugin-defer-stream | Transmite mediante codificación fragmentada HTTP |
| Prevención CSRF | @graphql-yoga/plugin-csrf-prevention | Requiere encabezado personalizado antes de analizar |
| GraphQL SSE | @graphql-yoga/plugin-graphql-sse | Modo SSE de conexión única |
</decision_framework>
<red_flags>
SEÑALES ROJAS
Alta Prioridad:
- Importar
GraphQLErrordesdegraphql-yogaen lugar degraphql-- paquete incorrecto, fallará - Pasar objetos
typeDefs/resolversdirectamente acreateYogasincreateSchema-- no compatible en v5 - Usar un plugin Envelop cuando exista un equivalente específico de Yoga -- se pierden las optimizaciones a nivel HTTP (la caché de respuestas de Yoga omite el análisis por completo; el equivalente de Envelop no puede)
- Lanzar un
Errorsimple en los resolutores esperando que los clientes vean el mensaje -- se enmascara como "Error inesperado." en producción
Prioridad Media:
- No configurar los orígenes CORS para producción -- el valor predeterminado es
*, que debe restringirse - Usar PubSub en memoria en múltiples instancias del servidor -- los eventos no se propagarán (usa
createRedisEventTargetrespaldado por Redis) - Falta la dependencia par
graphql--graphql-yogarequieregraphqlcomo par, instala ambos - Llamar a
createSchemasin ningún esquema -- Yoga requiere un esquema; no lo infiere
Casos Especiales y Trampas:
YogaInitialContext.requestes unaRequestde la API Fetch, no unIncomingMessagede Node.js -- usarequest.headers.get(), noreq.headers- El orden de ejecución de los plugins cambió en v5 -- los plugins agregados mediante
addPluginenonPluginInitahora se ejecutan inmediatamente después del plugin que los agrega, no al final - El callback
sessiondeuseResponseCachedebe devolver un string (ID de usuario) para ámbito PRIVADO onullpara público -- devolverundefinedrompe la caché - Las suscripciones SSE pasan por HTTP (text/event-stream) -- algunos proxies pueden almacenar eventos en búfer; establece
X-Accel-Buffering: nopara Nginx - El escalar
Fileen las cargas te da un objetoFilede WHATWG -- usa los métodos.text(),.arrayBuffer()o.stream()(noBufferde Node.js directamente) - El GraphiQL integrado de Yoga está habilitado por defecto -- deshabilítalo con
graphiql: falseen producción maskedErrorsestablecido enfalsedeshabilita TODO el enmascaramiento, incluidas las trazas de pila -- usa la función personalizadamaskErroren su lugar para exposición selectiva- CORS
credentials: trueconorigin: '*'es rechazado por los navegadores según la especificación Fetch -- especifica orígenes exactos
</red_flags>
<critical_reminders>
RECORDATORIOS CRÍTICOS
Todo el código debe seguir las convenciones del proyecto en CLAUDE.md
(DEBES importar GraphQLError desde 'graphql', NO desde 'graphql-yoga' -- es la exportación estándar de graphql-js)
(DEBES preferir plugins específicos de Yoga sobre equivalentes de Envelop -- los plugins de Yoga operan en la capa HTTP y pueden saltar la ejecución completa de GraphQL para resultados cacheados/persistidos)
(DEBES usar createSchema desde 'graphql-yoga' para esquema primero -- pasar objetos typeDefs/resolvers directamente a createYoga no es compatible en v5)
(DEBES usar constantes nombradas para todos los valores numéricos -- timeouts, TTLs, números de puerto, límites)
No seguir estas reglas causará errores de importación, problemas de enmascaramiento de errores, degradación del rendimiento o violaciones de seguridad.
</critical_reminders>