Modèles GraphQL Yoga
Guide rapide : Utilisez
createYoga+createSchemapour un serveur GraphQL compatible Fetch API fonctionnant sur n'importe quel runtime JS. Yoga v5 utilise Envelop pour la composition de plugins, SSE pour les souscriptions par défaut, le masquage intégré des erreurs et CORS prêt à l'emploi. ImportezGraphQLErrordepuisgraphql(pasgraphql-yoga) pour les erreurs intentionnelles côté client. Préférez les plugins spécifiques à Yoga aux équivalents Envelop pour les optimisations au niveau HTTP.
<exigences_critiques>
CRITIQUE : Avant d'Utiliser Cette Compétence
Tout code doit respecter les conventions du projet dans CLAUDE.md (kebab-case, exports nommés, ordre des imports,
import type, constantes nommées)
(Vous DEVEZ importer GraphQLError depuis 'graphql', PAS depuis 'graphql-yoga' — c'est l'export standard de graphql-js)
(Vous DEVEZ préférer les plugins spécifiques à Yoga aux équivalents Envelop — les plugins Yoga opèrent au niveau HTTP et peuvent ignorer totalement l'exécution GraphQL pour les résultats mis en cache/ persistés)
(Vous DEVEZ utiliser createSchema depuis 'graphql-yoga' pour les schémas SDL — passer des objets typeDefs/resolvers bruts directement à createYoga n'est pas supporté dans v5)
(Vous DEVEZ utiliser des constantes nommées pour toutes les valeurs numériques — timeouts, TTL, numéros de port, limites)
</exigences_critiques>
Auto-détection : GraphQL Yoga, graphql-yoga, createYoga, createSchema, createPubSub, Envelop, useResponseCache, useCSRFPrevention, usePersistedOperations, souscriptions GraphQL SSE, masquage d'erreurs, maskedErrors, graphql-ws, hooks de plugin Yoga, onRequest, onParams
Quand utiliser :
- Construire un serveur GraphQL devant fonctionner sur Node.js, Bun, Deno ou Cloudflare Workers
- API nécessitant des souscriptions via SSE (par défaut) ou WebSocket
- Étendre l'exécution GraphQL avec des plugins Envelop (mise en cache, authentification, journalisation)
- Téléchargements de fichiers utilisant la spécification GraphQL Multipart Request
- API de production nécessitant le masquage d'erreurs, CORS et la protection CSRF
Quand NE PAS utiliser :
- API REST uniquement sans besoins GraphQL
- CRUD simple où les gestionnaires de route intégrés d'un framework suffisent
- Lorsque vous avez besoin d'une passerelle fédérée (considérez une solution de passerelle dédiée)
Modèles clés couverts :
- Configuration du serveur avec
createYogaetcreateSchema(schéma d'abord) - Contexte typé avec génériques sur
createYoga<ServerContext> - Système de plugins Envelop : hooks de cycle de vie, plugins personnalisés, plugins spécifiques à Yoga
- Souscriptions : SSE (par défaut), WebSocket via
graphql-ws, PubSub intégré - Masquage d'erreurs et exposition intentionnelle de
GraphQLError - Téléchargements de fichiers avec le scalaire
FileWHATWG - Durcissement de production : CORS, prévention CSRF, GraphQL Armor, journalisation
- Déploiement multi-runtime : Node.js, Bun, Deno, Cloudflare Workers
Ressources détaillées :
- examples/core.md - Configuration du serveur, schéma, contexte, résolveurs, déploiement multi-runtime
- examples/plugins.md - Plugins Envelop, plugins personnalisés, hooks de cycle de vie
- examples/subscriptions.md - SSE, WebSocket, PubSub, filtrage
- examples/error-handling.md - Masquage d'erreurs, GraphQLError, masquage personnalisé
- examples/production.md - CORS, CSRF, mise en cache des réponses, opérations persistées, journalisation
- reference.md - Cadres de décision, référence des plugins, checklist de production
Philosophie
GraphQL Yoga est un serveur GraphQL clé en main, compatible Fetch API. Son cœur est construit sur l'API WHATWG Fetch (Request/Response), ce qui le rend indépendant du runtime — le même code serveur se déploie sur Node.js, Bun, Deno et les runtimes edge. Le système de plugins Envelop fournit un middleware composable aux niveaux HTTP et d'exécution GraphQL.
Approche du schéma : Yoga est indépendant de la bibliothèque de schéma. Utilisez createSchema (SDL d'abord), Pothos (code-first) ou vanilla graphql-js — tout ce qui produit un GraphQLSchema fonctionne.
Priorité des plugins : Lorsqu'un plugin Envelop et un plugin spécifique à Yoga existent pour la même fonctionnalité (mise en cache, opérations persistées, defer/stream), choisissez toujours la variante Yoga. Les plugins Yoga se branchent sur la couche HTTP et peuvent court-circuiter avant le début de l'exécution GraphQL, ignorant ainsi l'analyse et la validation pour les résultats en cache ou persistés.
Philosophie des erreurs : Toutes les erreurs inattendues sont masquées par défaut en production. Les erreurs intentionnelles sont levées en tant que GraphQLError du package graphql — elles contournent le masquage et atteignent les clients avec leur message et leurs extensions intactes.
Modèles principaux
Modèle 1 : Configuration du Serveur avec createYoga
Créez une instance Yoga avec createSchema pour les schémas basés sur SDL. L'instance yoga EST un gestionnaire d'API Fetch — passez-la directement au serveur HTTP de n'importe quel 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 }) => `Hello, ${name}!`,
},
},
}),
});
const server = createServer(yoga);
server.listen(PORT, () => {
console.info(`Server running on http://localhost:${PORT}/graphql`);
});
Pourquoi c'est bon : createSchema encapsule makeExecutableSchema, l'instance yoga est un gestionnaire Fetch standard, fonctionne sur n'importe quel runtime
Voir examples/core.md pour une configuration complète, un déploiement multi-runtime et un contexte typé.
Modèle 2 : Contexte Typé
Passez un générique à createYoga pour le typage du contexte spécifique au serveur. La fabrique context reçoit YogaInitialContext (contenant request et params) et retourne votre contexte personnalisé.
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 };
},
});
Pourquoi c'est bon : les types génériques circulent vers le paramètre de contexte du résolveur, request utilise l'API Fetch standard (pas req/res spécifique au framework)
Voir examples/core.md pour les modèles de contexte complets.
Modèle 3 : Système de Plugins Envelop
Les plugins sont passés dans le tableau plugins. Utilisez les plugins spécifiques à Yoga lorsqu'ils sont disponibles — ils opèrent au niveau HTTP et peuvent ignorer totalement l'exécution 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,
}),
],
});
Pourquoi c'est bon : le cache de réponse Yoga ignore l'analyse/validation pour les résultats mis en cache (l'équivalent Envelop ne le peut pas), les plugins se composent sans conflits
Voir examples/plugins.md pour les plugins personnalisés, les hooks de cycle de vie et tous les plugins spécifiques à Yoga.
Modèle 4 : Souscriptions avec SSE (Par Défaut)
Yoga utilise les Server-Sent Events par défaut pour les souscriptions — aucune configuration WebSocket nécessaire. Utilisez la syntaxe AsyncGenerator dans les résolveurs de souscription.
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 };
}
},
},
},
},
});
Pourquoi c'est bon : aucune infrastructure WebSocket nécessaire, fonctionne via les proxys HTTP et les équilibreurs de charge, bibliothèque graphql-sse pour les clients
Voir examples/subscriptions.md pour PubSub, configuration WebSocket et filtrage.
Modèle 5 : Masquage d'Erreurs et GraphQLError
Yoga masque toutes les erreurs inattendues par défaut. Lancez GraphQLError (depuis graphql) pour les erreurs intentionnelles côté client — elles contournent le masquage.
import { GraphQLError } from "graphql";
const NOT_FOUND_CODE = "USER_NOT_FOUND";
throw new GraphQLError("User not found", {
extensions: { code: NOT_FOUND_CODE },
});
Pourquoi c'est bon : les erreurs inattendues ne divulguent jamais les détails internes (détails de base de données, traces de pile), les erreurs intentionnelles passent avec le message + les extensions
Voir examples/error-handling.md pour le masquage personnalisé, la désactivation du masquage et le mode développement.
Modèle 6 : Téléchargements de Fichiers
Yoga supporte la spécification GraphQL Multipart Request. Ajoutez un scalaire File et recevez des objets File WHATWG dans les résolveurs.
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();
// Traiter le contenu du fichier
return true;
},
},
},
});
Pourquoi c'est bon : utilise l'API WHATWG File standard (identique au navigateur), aucun package supplémentaire nécessaire, désactivable avec multipart: false
Voir examples/core.md pour les modèles de téléchargement de fichiers complets.
<cadre_decision>
Cadre de Décision
Approche du Schéma
Besoin de types auto-générés à partir de SDL ?
+-- OUI --> createSchema (schéma d'abord avec typeDefs + resolvers)
+-- NON --> Souhaitez-vous une inférence TypeScript complète dans la définition du schéma ?
+-- OUI --> Bibliothèque code-first (ex. Pothos) — passez le GraphQLSchema résultant à Yoga
+-- NON --> GraphQLSchema vanilla graphql-js
Transport de Souscription
Besoin de souscriptions ?
+-- OUI --> Les clients ont-ils besoin de communication bidirectionnelle ?
| +-- OUI --> WebSocket via graphql-ws (ajoutez les packages ws + graphql-ws)
| +-- NON --> SSE (par défaut, zéro configuration, fonctionne via les proxys)
+-- NON --> Aucune configuration de souscription nécessaire
Sélection de Plugin
Fonctionnalité disponible en tant que plugin spécifique à Yoga ?
+-- OUI --> Utilisez le plugin Yoga (hooks au niveau HTTP, peut ignorer l'exécution)
+-- NON --> Utilisez le plugin Envelop (hooks au niveau de l'exécution GraphQL)
Plugins Spécifiques à Yoga (Préférez aux Envelop)
| Plugin | Package | Pourquoi Spécifique à Yoga |
|---|---|---|
| Cache de Réponse | @graphql-yoga/plugin-response-cache | Ignore l'exécution pour les requêtes en cache |
| Opérations Persistées | @graphql-yoga/plugin-persisted-operations | Rejette les opérations inconnues au niveau HTTP |
| Defer/Stream | @graphql-yoga/plugin-defer-stream | Streame via l'encodage par morceaux HTTP |
| Prévention CSRF | @graphql-yoga/plugin-csrf-prevention | Nécessite un en-tête personnalisé avant l'analyse |
| GraphQL SSE | @graphql-yoga/plugin-graphql-sse | Mode SSE à connexion unique |
</cadre_decision>
<drapeaux_rouges>
DRAPEAUX ROUGES
Haute Priorité :
- Importer
GraphQLErrordepuisgraphql-yogaau lieu degraphql— mauvais package, échouera - Passer des objets
typeDefs/resolversdirectement àcreateYogasanscreateSchema— non supporté dans v5 - Utiliser un plugin Envelop alors qu'un équivalent spécifique à Yoga existe — manque les optimisations au niveau HTTP (le cache de réponse Yoga ignore totalement l'analyse ; l'équivalent Envelop ne le peut pas)
- Lancer une
Errorsimple dans les résolveurs en s'attendant à ce que les clients voient le message — masqué en "Erreur inattendue." en production
Priorité Moyenne :
- Ne pas configurer les origines CORS pour la production — la valeur par défaut est
*, qui doit être restreinte - Utiliser PubSub en mémoire sur plusieurs instances de serveur — les événements ne se propageront pas (utilisez
createRedisEventTargetbasé sur Redis) - Dépendance
graphqlmanquante —graphql-yoganécessitegraphqlcomme dépendance, installez les deux - Appeler
createSchemasans schéma du tout — Yoga nécessite un schéma ; il n'en infère pas
Pièges et Cas Limites :
YogaInitialContext.requestest uneRequestde l'API Fetch, pas unIncomingMessageNode.js — utilisezrequest.headers.get(), pasreq.headers- L'ordre d'exécution des plugins a changé dans v5 — les plugins ajoutés via
addPlugindansonPluginInits'exécutent maintenant immédiatement après le plugin ajouteur, pas en dernier - Le callback
sessiondeuseResponseCachedoit retourner une chaîne (ID utilisateur) pour la portée PRIVÉE ounullpour publique — retournerundefinedcasse la mise en cache - Les souscriptions SSE passent par HTTP (text/event-stream) — certains proxys peuvent bufferiser les événements ; définissez
X-Accel-Buffering: nopour Nginx - Le scalaire
Filedans les téléchargements vous donne un objet WHATWGFile— utilisez les méthodes.text(),.arrayBuffer()ou.stream()(pasBufferNode.js directement) - Le GraphiQL intégré de Yoga est activé par défaut — désactivez avec
graphiql: falseen production maskedErrorsdéfini surfalsedésactive TOUT le masquage, y compris les traces de pile — utilisez plutôt la fonction personnaliséemaskErrorpour une exposition sélectivecredentials: truede CORS avecorigin: '*'est rejeté par les navigateurs selon la spécification Fetch — spécifiez des origines exactes
</drapeaux_rouges>
<rappels_critiques>
RAPPELS CRITIQUES
Tout code doit respecter les conventions du projet dans CLAUDE.md
(Vous DEVEZ importer GraphQLError depuis 'graphql', PAS depuis 'graphql-yoga' — c'est l'export standard de graphql-js)
(Vous DEVEZ préférer les plugins spécifiques à Yoga aux équivalents Envelop — les plugins Yoga opèrent au niveau HTTP et peuvent ignorer totalement l'exécution GraphQL pour les résultats mis en cache/persistés)
(Vous DEVEZ utiliser createSchema depuis 'graphql-yoga' pour les schémas SDL — passer des objets typeDefs/resolvers bruts directement à createYoga n'est pas supporté dans v5)
(Vous DEVEZ utiliser des constantes nommées pour toutes les valeurs numériques — timeouts, TTL, numéros de port, limites)
Le non-respect de ces règles entraînera des échecs d'importation, des résultats inattendus ou des vulnérabilités de sécurité.
</rappels_critiques>
Récupération
Problèmes Courants
| Symptôme | Cause | Solution |
|---|---|---|
GraphQLError is not a constructor | Importé depuis graphql-yoga au lieu de graphql | Remplacez l'import par import { GraphQLError } from "graphql" |
| Toutes les erreurs apparaissent comme "Erreur inattendue." | GraphQLError importé depuis le mauvais package OU maskedErrors est activé et vous lancez Error | Vérifiez l'import de GraphQLError, utilisez GraphQLError pour les erreurs intentionnelles |
| Les résolveurs ne reçoivent pas les types de contexte | Le générique de createYoga est manquant ou ne correspond pas | Ajoutez un générique : createYoga<ServerContext>, vérifiez qu'il correspond à l'interface |
| Les souscriptions SSE ne fonctionnent pas derrière un proxy | Le proxy bufferise le flux d'événements | Ajoutez l'en-tête X-Accel-Buffering: no (Nginx) ou Cache-Control: no-cache |
createYoga se plaint de typeDefs inconnu | Dans v5, typeDefs doit être passé dans createSchema | Enveloppez avec createSchema({ typeDefs, resolvers }) |
Voir reference.md pour un dépannage complet.