GraphQL Yoga Patterns
Kurzanleitung: Verwenden Sie
createYoga+createSchemafür einen Fetch-API-kompatiblen GraphQL-Server, der auf jeder JS-Laufzeitumgebung läuft. Yoga v5 nutzt Envelop für die Plugin-Komposition, SSE für Subscriptions standardmäßig, eine eingebaute Fehlermaskierung und CORS out of the box. Importieren SieGraphQLErrorausgraphql(nicht ausgraphql-yoga) für beabsichtigte clientseitige Fehler. Bevorzugen Sie Yoga-spezifische Plugins gegenüber Envelop-Äquivalenten für HTTP-Level-Optimierungen.
<critical_requirements>
KRITISCH: Vor der Nutzung dieser Skill
Der gesamte Code muss den Projektkonventionen in CLAUDE.md folgen (kebab-case, benannte Exporte, Importsortierung,
import type, benannte Konstanten)
(Sie MÜSSEN GraphQLError aus 'graphql' importieren, NICHT aus 'graphql-yoga' – es ist der Standard-Export von graphql-js)
(Sie MÜSSEN Yoga-spezifische Plugins gegenüber Envelop-Äquivalenten bevorzugen – Yoga-Plugins arbeiten auf der HTTP-Ebene und können die GraphQL-Ausführung für zwischengespeicherte/persistierte Ergebnisse vollständig überspringen)
(Sie MÜSSEN createSchema aus 'graphql-yoga' für schema-first verwenden – das direkte Übergeben von rohen typeDefs/resolvers-Objekten an createYoga wird in v5 nicht unterstützt)
(Sie MÜSSEN benannte Konstanten für alle numerischen Werte verwenden – Timeouts, TTLs, Portnummern, Limits)
</critical_requirements>
Auto-Erkennung: GraphQL Yoga, graphql-yoga, createYoga, createSchema, createPubSub, Envelop, useResponseCache, useCSRFPrevention, usePersistedOperations, GraphQL Subscriptions SSE, Fehlermaskierung, maskedErrors, graphql-ws, Yoga Plugin Hooks, onRequest, onParams
Wann verwenden:
- Aufbau eines GraphQL-Servers, der auf Node.js, Bun, Deno oder Cloudflare Workers laufen soll
- APIs, die Subscriptions via SSE (Standard) oder WebSocket benötigen
- Erweiterung der GraphQL-Ausführung mit Envelop-Plugins (Caching, Authentifizierung, Logging)
- Dateiuploads mit dem GraphQL Multipart Request Spec
- Produktions-APIs, die Fehlermaskierung, CORS und CSRF-Schutz benötigen
Wann NICHT verwenden:
- Nur-REST-APIs ohne GraphQL-Bedarf
- Einfaches CRUD, bei dem die integrierten Routen-Handler eines Frameworks ausreichen
- Wenn Sie ein föderiertes Gateway benötigen (erwägen Sie eine dedizierte Gateway-Lösung)
Abgedeckte Schlüsselmuster:
- Server-Setup mit
createYogaundcreateSchema(schema-first) - Typsicherer Kontext mit Generics auf
createYoga<ServerContext> - Envelop-Plugin-System: Lifecycle-Hooks, benutzerdefinierte Plugins, Yoga-spezifische Plugins
- Subscriptions: SSE (Standard), WebSocket via
graphql-ws, integriertes PubSub - Fehlermaskierung und beabsichtigte
GraphQLError-Exposition - Dateiuploads mit WHATWG
File-Skalar - Produktionshärtung: CORS, CSRF-Prävention, GraphQL Armor, Logging
- Cross-Runtime-Bereitstellung: Node.js, Bun, Deno, Cloudflare Workers
Detailierte Ressourcen:
- examples/core.md - Server-Setup, Schema, Kontext, Resolver, Cross-Runtime-Bereitstellung
- examples/plugins.md - Envelop-Plugins, benutzerdefinierte Plugins, Lifecycle-Hooks
- examples/subscriptions.md - SSE, WebSocket, PubSub, Filterung
- examples/error-handling.md - Fehlermaskierung, GraphQLError, benutzerdefinierte Maskierung
- examples/production.md - CORS, CSRF, Antwort-Caching, persistierte Operationen, Logging
- reference.md - Entscheidungsrahmen, Plugin-Referenz, Produktions-Checkliste
Philosophie
GraphQL Yoga ist ein batteriebetriebener, Fetch-API-kompatibler GraphQL-Server. Sein Kern basiert auf der WHATWG-Fetch-API (Request/Response), was ihn laufzeitunabhängig macht – derselbe Server-Code wird auf Node.js, Bun, Deno und Edge-Laufzeiten bereitgestellt. Das Envelop-Plugin-System bietet zusammensetzbare Middleware sowohl auf der HTTP- als auch auf der GraphQL-Ausführungsebene.
Schema-Ansatz: Yoga ist schema-bibliotheksunabhängig. Verwenden Sie createSchema (schema-first SDL), Pothos (code-first) oder reines graphql-js – alles, was ein GraphQLSchema erzeugt, funktioniert.
Plugin-Priorität: Wenn sowohl ein Envelop-Plugin als auch ein Yoga-spezifisches Plugin für dieselbe Funktion existieren (Caching, persistierte Operationen, defer/stream), wählen Sie immer die Yoga-Variante. Yoga-Plugins haken in die HTTP-Ebene ein und können die Verarbeitung vor der GraphQL-Ausführung unterbrechen, wobei sie das Parsen und Validieren für zwischengespeicherte oder persistierte Ergebnisse vollständig überspringen.
Fehlerphilosophie: Alle unerwarteten Fehler werden standardmäßig in der Produktion maskiert. Beabsichtigte Fehler werden als GraphQLError aus dem graphql-Paket geworfen – diese umgehen die Maskierung und erreichen Clients mit ihrer Nachricht und Erweiterungen intakt.
Kernmuster
Muster 1: Server-Setup mit createYoga
Erstellen Sie eine Yoga-Instanz mit createSchema für SDL-basierte Schemas. Die Yoga-Instanz IST ein Fetch-API-Handler – übergeben Sie sie direkt an den HTTP-Server einer beliebigen Laufzeitumgebung.
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`);
});
Warum gut: createSchema kapselt makeExecutableSchema, die Yoga-Instanz ist ein standardmäßiger Fetch-Handler, funktioniert auf jeder Laufzeitumgebung
Siehe examples/core.md für vollständiges Setup, Cross-Runtime-Bereitstellung und typsicheren Kontext.
Muster 2: Typsicherer Kontext
Übergeben Sie ein Generic an createYoga für die serverseitige Kontexttypisierung. Die context-Factory erhält YogaInitialContext (enthält request und params) und gibt Ihren benutzerdefinierten Kontext zurück.
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 };
},
});
Warum gut: Generics fließen in den Resolver-Kontext-Parameter, request verwendet die standardmäßige Fetch-API (nicht frameworkspezifisches req/res)
Siehe examples/core.md für vollständige Kontextmuster.
Muster 3: Envelop-Plugin-System
Plugins werden im plugins-Array übergeben. Verwenden Sie Yoga-spezifische Plugins, wenn verfügbar – sie arbeiten auf der HTTP-Ebene und können die GraphQL-Ausführung vollständig überspringen.
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,
}),
],
});
Warum gut: Der Yoga-Antwort-Cache überspringt das Parsen/Validieren für zwischengespeicherte Ergebnisse (das Envelop-Äquivalent kann das nicht), Plugins komponieren ohne Konflikte
Siehe examples/plugins.md für benutzerdefinierte Plugins, Lifecycle-Hooks und alle Yoga-spezifischen Plugins.
Muster 4: Subscriptions mit SSE (Standard)
Yoga verwendet standardmäßig Server-Sent Events für Subscriptions – kein WebSocket-Setup erforderlich. Verwenden Sie die AsyncGenerator-Syntax in Subscription-Resolvern.
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 };
}
},
},
},
},
});
Warum gut: keine WebSocket-Infrastruktur erforderlich, funktioniert durch HTTP-Proxies und Load-Balancer, graphql-sse-Bibliothek für Clients
Siehe examples/subscriptions.md für PubSub, WebSocket-Setup und Filterung.
Muster 5: Fehlermaskierung und GraphQLError
Yoga maskiert standardmäßig alle unerwarteten Fehler. Werfen Sie GraphQLError (aus graphql) für beabsichtigte clientseitige Fehler – diese umgehen die Maskierung.
import { GraphQLError } from "graphql";
const NOT_FOUND_CODE = "USER_NOT_FOUND";
throw new GraphQLError("User not found", {
extensions: { code: NOT_FOUND_CODE },
});
Warum gut: unerwartete Fehler geben niemals Interna preis (Datenbankdetails, Stack-Traces), beabsichtigte Fehler werden mit Nachricht + Erweiterungen durchgelassen
Siehe examples/error-handling.md für benutzerdefinierte Maskierung, Deaktivierung der Maskierung und den Entwicklungsmodus.
Muster 6: Dateiuploads
Yoga unterstützt den GraphQL Multipart Request Spec. Fügen Sie einen File-Skalar hinzu und empfangen Sie WHATWG File-Objekte in Resolvern.
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();
// Dateiinhalt verarbeiten
return true;
},
},
},
});
Warum gut: verwendet die standardmäßige WHATWG-File-API (wie im Browser), keine zusätzlichen Pakete erforderlich, deaktivieren mit multipart: false
Siehe examples/core.md für vollständige Dateiupload-Muster.
<decision_framework>
Entscheidungsrahmen
Schema-Ansatz
Auto-generierte Typen aus SDL benötigt?
+-- JA --> createSchema (schema-first mit typeDefs + resolvers)
+-- NEIN --> Vollständige TypeScript-Inferenz in der Schema-Definition gewünscht?
+-- JA --> Code-first-Bibliothek (z.B. Pothos) – resultierendes GraphQLSchema an Yoga übergeben
+-- NEIN --> Reines graphql-js GraphQLSchema
Subscription Transport
Subscriptions benötigt?
+-- JA --> Benötigen Clients bidirektionale Kommunikation?
| +-- JA --> WebSocket via graphql-ws (Pakete ws + graphql-ws hinzufügen)
| +-- NEIN --> SSE (Standard, null Konfiguration, funktioniert durch Proxies)
+-- NEIN --> Kein Subscription-Setup erforderlich
Plugin-Auswahl
Funktion als Yoga-spezifisches Plugin verfügbar?
+-- JA --> Yoga-Plugin verwenden (HTTP-Level-Hooks, kann Ausführung überspringen)
+-- NEIN --> Envelop-Plugin verwenden (GraphQL-Ausführungs-Level-Hooks)
Yoga-spezifische Plugins (Envelop vorziehen)
| Plugin | Paket | Warum Yoga-spezifisch |
|---|---|---|
| Response Cache | @graphql-yoga/plugin-response-cache | Überspringt Ausführung für gecachte Abfragen |
| Persisted Operations | @graphql-yoga/plugin-persisted-operations | Lehnt unbekannte Operationen auf HTTP-Ebene ab |
| Defer/Stream | @graphql-yoga/plugin-defer-stream | Streamt via HTTP Chunked Encoding |
| CSRF Prevention | @graphql-yoga/plugin-csrf-prevention | Erfordert benutzerdefinierten Header vor dem Parsen |
| GraphQL SSE | @graphql-yoga/plugin-graphql-sse | Einzelverbindungs-SSE-Modus |
</decision_framework>
<red_flags>
ROTE FLAGGEN
Hohe Priorität:
- Importieren von
GraphQLErrorausgraphql-yogaanstatt ausgraphql– falsches Paket, wird fehlschlagen - Direktes Übergeben von
typeDefs/resolvers-Objekten ancreateYogaohnecreateSchema– wird in v5 nicht unterstützt - Verwenden eines Envelop-Plugins, wenn ein Yoga-spezifisches Äquivalent existiert – verpasst HTTP-Level-Optimierungen (der Yoga-Antwort-Cache überspringt das Parsen vollständig; das Envelop-Äquivalent kann das nicht)
- Werfen eines einfachen
Errorin Resolvern in der Erwartung, dass Clients die Nachricht sehen – wird in der Produktion zu "Unerwarteter Fehler." maskiert
Mittlere Priorität:
- Nicht Konfigurieren von CORS-Ursprüngen für die Produktion – Standard ist
*, das eingeschränkt werden sollte - Verwenden von In-Memory-PubSub über mehrere Serverinstanzen hinweg – Ereignisse werden nicht weitergeleitet (verwenden Sie Redis-gestütztes
createRedisEventTarget) - Fehlende
graphql-Peer-Abhängigkeit –graphql-yogabenötigtgraphqlals Peer, installieren Sie beide - Aufrufen von
createSchemaohne Schema überhaupt – Yoga benötigt ein Schema; es leitet keines ab
Tücken & Randfälle:
YogaInitialContext.requestist ein Fetch-API-Request, kein Node.js-IncomingMessage– verwenden Sierequest.headers.get(), nichtreq.headers- Die Plugin-Ausführungsreihenfolge hat sich in v5 geändert – Plugins, die über
addPlugininonPluginInithinzugefügt werden, werden jetzt unmittelbar nach dem hinzufügenden Plugin ausgeführt, nicht als letztes - Der
session-Rückruf vonuseResponseCachemuss einen String (Benutzer-ID) für den PRIVATEN Bereich odernullfür öffentlich zurückgeben – das Zurückgeben vonundefinedbricht das Caching - SSE-Subscriptions gehen durch HTTP (text/event-stream) – einige Proxys puffern Ereignisse; setzen Sie
X-Accel-Buffering: nofür Nginx - Der
File-Skalar bei Uploads liefert ein WHATWG-File-Objekt – verwenden Sie die Methoden.text(),.arrayBuffer()oder.stream()(nicht direkt Node.jsBuffer) - Yogas eingebautes GraphiQL ist standardmäßig aktiviert – deaktivieren Sie es mit
graphiql: falsein der Produktion maskedErrorsauffalsegesetzt deaktiviert die gesamte Maskierung einschließlich Stack-Traces – verwenden Sie stattdessen die benutzerdefiniertemaskError-Funktion für selektive Exposition- CORS
credentials: truemitorigin: '*'wird von Browsern laut Fetch-Spezifikation abgelehnt – geben Sie exakte Ursprünge an
</red_flags>
<critical_reminders>
KRITISCHE ERINNERUNGEN
Der gesamte Code muss den Projektkonventionen in CLAUDE.md folgen
(Sie MÜSSEN GraphQLError aus 'graphql' importieren, NICHT aus 'graphql-yoga' – es ist der Standard-Export von graphql-js)
(Sie MÜSSEN Yoga-spezifische Plugins gegenüber Envelop-Äquivalenten bevorzugen – Yoga-Plugins arbeiten auf der HTTP-Ebene und können die GraphQL-Ausführung für zwischengespeicherte/persistierte Ergebnisse vollständig überspringen)
(Sie MÜSSEN createSchema aus 'graphql-yoga' für schema-first verwenden – das direkte Übergeben von rohen typeDefs/resolvers-Objekten an createYoga wird in v5 nicht unterstützt)
(Sie MÜSSEN benannte Konstanten für alle numerischen Werte verwenden – Timeouts, TTLs, Portnummern, Limits)
Die Nichtbeachtung dieser Regeln führt zu Importfehlern, Sicherheitslücken oder Laufzeitfehlern.
Nächste Schritte
- Sofort lesen → examples/core.md (grundlegendes Server-Setup, Schema, Resolver, Cross-Runtime-Einsatz)
- Plugin-Integration → examples/plugins.md (Yoga- und Envelop-Plugins, Lifecycle-Hooks, benutzerdefinierte Plugins)
- Subscriptions → examples/subscriptions.md (SSE, WebSocket, PubSub, Filterung)
- Fehlerbehandlung → examples/error-handling.md (Maskierung, GraphQLError, benutzerdefinierte Maskierung)
- Produktion → examples/production.md (CORS, CSRF, Caching, Persistierte Operationen, Logging)
- Referenz → reference.md (Entscheidungsrahmen, Plugin-Referenz, Produktions-Checkliste)
</critical_reminders>
Wenn Sie unsicher sind, welches Muster Sie verwenden sollen, lesen Sie die Beispiele und Referenzen. Die Muster in diesem Dokument bauen aufeinander auf, wobei spätere Beispiele Kenntnisse aus früheren voraussetzen.