GraphQL Yoga パターン は何をしますか?
クイックガイド:
createYoga+createSchemaを使用して、あらゆるJSランタイムで動作するFetch API互換のGraphQLサーバーを構築します。Yoga v5はEnvelopを使用したプラグイン構成、SSEによるデフォルトのサブスクリプション、組み込みのエラーマスキング、CORSを標準で提供します。クライアント向けの意図的なエラーは、graphql-yogaではなくgraphqlからGraphQLErrorをインポートしてスローします。HTTPレベルの最適化には、Envelop相当のプラグインよりもYoga固有のプラグインを優先します。
<critical_requirements>
重要: このスキルを使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従うこと(ケバブケース、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(GraphQLError は 'graphql' からインポートすること。'graphql-yoga' からはインポートしないでください。これは標準の graphql-js エクスポートです)
(Envelop相当のプラグインよりもYoga固有のプラグインを優先すること。YogaプラグインはHTTPレイヤーで動作し、キャッシュ/永続化された結果のためにGraphQLの実行を完全にスキップできます)
(スキーマファーストの場合は 'graphql-yoga' の createSchema を使用すること。v5では createYoga に直接 typeDefs/resolvers オブジェクトを渡すことはサポートされていません)
(すべての数値には名前付き定数を使用すること。タイムアウト、TTL、ポート番号、制限値など)
</critical_requirements>
自動検出: GraphQL Yoga、graphql-yoga、createYoga、createSchema、createPubSub、Envelop、useResponseCache、useCSRFPrevention、usePersistedOperations、GraphQL サブスクリプション SSE、エラーマスキング、maskedErrors、graphql-ws、Yoga プラグインフック、onRequest、onParams
使用すべきケース:
- Node.js、Bun、Deno、Cloudflare Workers で動作するGraphQLサーバーを構築する場合
- SSE(デフォルト)またはWebSocketを介したサブスクリプションが必要なAPI
- Envelopプラグイン(キャッシュ、認証、ログ記録)でGraphQL実行を拡張する場合
- GraphQL Multipart Request仕様を使用したファイルアップロード
- エラーマスキング、CORS、CSRF保護が必要な本番API
使用すべきでないケース:
- GraphQLを必要としないREST専用API
- フレームワークの組み込みルートハンドラで十分な単純なCRUD
- フェデレーションゲートウェイが必要な場合(専用ゲートウェイソリューションを検討)
カバーする主要パターン:
createYogaとcreateSchemaを使用したサーバーセットアップ(スキーマファースト)createYoga<ServerContext>のジェネリクスによる型安全なコンテキスト- Envelopプラグインシステム: ライフサイクルフック、カスタムプラグイン、Yoga固有のプラグイン
- サブスクリプション: SSE(デフォルト)、
graphql-wsによるWebSocket、組み込みPubSub - エラーマスキングと意図的な
GraphQLErrorの公開 - WHATWG
Fileスカラーを使用したファイルアップロード - 本番運用の強化: CORS、CSRF防止、GraphQL Armor、ログ記録
- クロスランタイムデプロイ: Node.js、Bun、Deno、Cloudflare Workers
詳細リソース:
- examples/core.md - サーバーセットアップ、スキーマ、コンテキスト、リゾルバー、クロスランタイムデプロイ
- examples/plugins.md - Envelopプラグイン、カスタムプラグイン、ライフサイクルフック
- examples/subscriptions.md - SSE、WebSocket、PubSub、フィルタリング
- examples/error-handling.md - エラーマスキング、GraphQLError、カスタムマスキング
- examples/production.md - CORS、CSRF、レスポンスキャッシュ、永続化操作、ログ記録
- reference.md - 決定フレームワーク、プラグインリファレンス、本番チェックリスト
哲学
GraphQL Yogaは、何でも含まれている、Fetch API互換のGraphQLサーバーです。そのコアはWHATWG Fetch API(Request/Response)に基づいており、ランタイムに依存しません。つまり、同じサーバーコードをNode.js、Bun、Deno、そしてエッジランタイムにデプロイできます。Envelopプラグインシステムは、HTTPレイヤーとGraphQL実行レイヤーの両方で合成可能なミドルウェアを提供します。
スキーマアプローチ: Yogaはスキーマライブラリに依存しません。createSchema(スキーマファーストSDL)、Pothos(コードファースト)、またはバニラの graphql-js など、GraphQLSchema を生成するものなら何でも動作します。
プラグインの優先順位: 同じ機能(キャッシュ、永続化操作、defer/stream)に対してEnvelopプラグインとYoga固有のプラグインの両方が存在する場合は、常にYogaバリアントを選択します。YogaプラグインはHTTPレイヤーにフックし、キャッシュまたは永続化された結果の場合、GraphQL実行が開始される前にショートサーキットして、パースとバリデーションを完全にスキップできます。
エラーに関する哲学: 本番環境では、すべての予期しないエラーはデフォルトでマスクされます。意図的なエラーは graphql パッケージから GraphQLError としてスローされます。これらはマスキングをバイパスし、メッセージと拡張機能をそのままクライアントに到達させます。
コアパターン
パターン 1: createYoga によるサーバーセットアップ
SDLベースのスキーマには createSchema を使用してYogaインスタンスを作成します。yogaインスタンスはFetch APIハンドラそのものです。任意のランタイムのHTTPサーバーに直接渡します。
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`);
});
良い理由: createSchema は makeExecutableSchema をラップし、yogaインスタンスは標準のFetchハンドラであり、任意のランタイムで動作します
完全なセットアップ、クロスランタイムデプロイ、型安全なコンテキストについては、examples/core.md を参照してください。
パターン 2: 型安全なコンテキスト
サーバー固有のコンテキストタイピングのために createYoga にジェネリックを渡します。context ファクトリは YogaInitialContext(request と params を含む)を受け取り、カスタムコンテキストを返します。
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 };
},
});
良い理由: ジェネリック型はリゾルバーのコンテキストパラメーターに伝播し、request は標準のFetch APIを使用します(フレームワーク固有の req/res ではありません)
完全なコンテキストパターンについては、examples/core.md を参照してください。
パターン 3: Envelop プラグインシステム
プラグインは plugins 配列で渡します。利用可能な場合はYoga固有のプラグインを使用します。これらはHTTPレイヤーで動作し、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,
}),
],
});
良い理由: Yogaのレスポンスキャッシュは、キャッシュされた結果に対してパース/バリデーションをスキップします(Envelop相当のものはできません)。プラグインは競合なく構成できます。
カスタムプラグイン、ライフサイクルフック、およびすべてのYoga固有プラグインについては、examples/plugins.md を参照してください。
パターン 4: SSE を使用したサブスクリプション(デフォルト)
YogaはデフォルトでサブスクリプションにServer-Sent Eventsを使用します。WebSocketのセットアップは不要です。サブスクリプションリゾルバーでは AsyncGenerator 構文を使用します。
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 };
}
},
},
},
},
});
良い理由: WebSocketインフラが不要で、HTTPプロキシやロードバランサーを介して動作し、クライアント向けには graphql-sse ライブラリが利用可能です
PubSub、WebSocketセットアップ、フィルタリングについては、examples/subscriptions.md を参照してください。
パターン 5: エラーマスキングと GraphQLError
Yogaはデフォルトですべての予期しないエラーをマスクします。クライアント向けの意図的なエラーには、graphql から GraphQLError をスローします。これらはマスキングをバイパスします。
import { GraphQLError } from "graphql";
const NOT_FOUND_CODE = "USER_NOT_FOUND";
throw new GraphQLError("User not found", {
extensions: { code: NOT_FOUND_CODE },
});
良い理由: 予期しないエラーが内部情報(データベースの詳細、スタックトレース)を漏洩することはなく、意図的なエラーはメッセージと拡張機能とともに通過します
カスタムマスキング、マスキングの無効化、開発モードについては、examples/error-handling.md を参照してください。
パターン 6: ファイルアップロード
YogaはGraphQL Multipart Request仕様をサポートしています。File スカラーを追加し、リゾルバーでWHATWG File オブジェクトを受け取ります。
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();
// ファイル内容を処理
return true;
},
},
},
});
良い理由: 標準のWHATWG File API(ブラウザと同じ)を使用し、追加のパッケージは不要で、multipart: false で無効化できます
完全なファイルアップロードパターンについては、examples/core.md を参照してください。
<decision_framework>
決定フレームワーク
スキーマアプローチ
SDLから自動生成された型が必要ですか?
+-- YES --> createSchema(typeDefs + resolversによるスキーマファースト)
+-- NO --> スキーマ定義で完全なTypeScript推論が必要ですか?
+-- YES --> コードファーストライブラリ(例: Pothos)-- 結果の GraphQLSchema を Yoga に渡す
+-- NO --> バニラ graphql-js GraphQLSchema
サブスクリプション転送
サブスクリプションが必要ですか?
+-- YES --> クライアントに双方向通信が必要ですか?
| +-- YES --> graphql-ws 経由の WebSocket(ws + graphql-ws パッケージを追加)
| +-- NO --> SSE(デフォルト、ゼロ設定、プロキシ経由で動作)
+-- NO --> サブスクリプションのセットアップは不要
プラグイン選択
機能がYoga固有のプラグインとして利用可能ですか?
+-- YES --> Yogaプラグインを使用(HTTPレベルのフック、実行をスキップ可能)
+-- NO --> Envelopプラグインを使用(GraphQL実行レベルのフック)
Yoga固有プラグイン(Envelopより優先)
| プラグイン | パッケージ | Yoga固有の理由 |
|---|---|---|
| レスポンスキャッシュ | @graphql-yoga/plugin-response-cache | キャッシュされたクエリの実行をスキップ |
| 永続化操作 | @graphql-yoga/plugin-persisted-operations | HTTPレイヤーで未知の操作を拒否 |
| Defer/Stream | @graphql-yoga/plugin-defer-stream | HTTPチャンクエンコーディングでストリーミング |
| CSRF防止 | @graphql-yoga/plugin-csrf-prevention | パース前にカスタムヘッダーを要求 |
| GraphQL SSE | @graphql-yoga/plugin-graphql-sse | 単一接続SSEモード |
</decision_framework>
<red_flags>
注意すべき点(レッドフラグ)
高優先度:
GraphQLErrorをgraphql-yogaからインポートしている(graphqlからではない)-- パッケージが間違っており、失敗しますtypeDefs/resolversオブジェクトをcreateSchemaなしで直接createYogaに渡している -- v5ではサポートされていません- Yoga固有の同等プラグインが存在するのにEnvelopプラグインを使用している -- HTTPレベルの最適化を逃します(Yogaのレスポンスキャッシュはパースを完全にスキップしますが、Envelopの同等品はできません)
- リゾルバーでプレーンな
Errorをスローし、クライアントがメッセージを見ることを期待している -- 本番環境では「予期しないエラーが発生しました。」にマスクされます
中優先度:
- 本番環境でCORSオリジンを設定していない -- デフォルトは
*で、制限する必要があります - 複数のサーバーインスタンス間でインメモリPubSubを使用している -- イベントが伝播しません(Redisバックアップの
createRedisEventTargetを使用) graphqlピア依存関係がない --graphql-yogaはgraphqlをピアとして必要とします。両方をインストールしてください- スキーマなしで
createSchemaを呼び出している -- Yogaはスキーマを必要とします。推論はしません
落とし穴とエッジケース:
YogaInitialContext.requestはFetch APIのRequestであり、Node.jsのIncomingMessageではありません。req.headersではなくrequest.headers.get()を使用します- v5でプラグイン実行順序が変更されました --
onPluginInitでaddPluginを介して追加されたプラグインは、追加元のプラグインの直後(最後ではない)に実行されるようになりました useResponseCacheのsessionコールバックは、PRIVATEスコープの場合は文字列(ユーザーID)、publicの場合はnullを返す必要があります。undefinedを返すとキャッシュが壊れます- SSEサブスクリプションはHTTP(text/event-stream)を通過します。一部のプロキシはイベントをバッファリングする場合があります。Nginxの場合は
X-Accel-Buffering: noを設定します - アップロードの
FileスカラーはWHATWGのFileオブジェクトを提供します。.text()、.arrayBuffer()、または.stream()メソッドを使用します(Node.jsのBufferを直接使用しない) - Yogaの組み込みGraphiQLはデフォルトで有効です。本番環境では
graphiql: falseで無効にします maskedErrorsをfalseに設定すると、スタックトレースを含むすべてのマスキングが無効になります。代わりにカスタムのmaskError関数を使用して選択的に公開します- CORSで
credentials: trueとorigin: '*'を組み合わせると、Fetch仕様に従ってブラウザによって拒否されます。正確なオリジンを指定します
</red_flags>
<critical_reminders>
重要なリマインダー
すべてのコードは CLAUDE.md のプロジェクト規約に従うこと
(GraphQLError は 'graphql' からインポートすること。'graphql-yoga' からはインポートしないでください。これは標準の graphql-js エクスポートです)
(Envelop相当のプラグインよりもYoga固有のプラグインを優先すること。YogaプラグインはHTTPレイヤーで動作し、キャッシュ/永続化された結果のためにGraphQLの実行を完全にスキップできます)
(スキーマファーストの場合は 'graphql-yoga' の createSchema を使用すること。v5では createYoga に直接 typeDefs/resolvers オブジェクトを渡すことはサポートされていません)
(すべての数値には名前付き定数を使用すること。タイムアウト、TTL、ポート番号、制限値など)
これらのルールを遵守しないと、インポートエラー、実行時エラー、または重大なタイプミスが発生します。
</critical_reminders>