dist/plugins/api-graphql-yoga/skills/api-graphql-yoga/SKILL.md
GraphQL Yoga v5 server, Envelop plugins, subscriptions, error masking
npx skillsauth add agents-inc/skills api-graphql-yogaInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Use
createYoga+createSchemafor a Fetch API-compatible GraphQL server that runs on any JS runtime. Yoga v5 uses Envelop for plugin composition, SSE for subscriptions by default, built-in error masking, and CORS out of the box. ImportGraphQLErrorfromgraphql(notgraphql-yoga) for intentional client-facing errors. Prefer Yoga-specific plugins over Envelop equivalents for HTTP-level optimizations.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST import GraphQLError from 'graphql', NOT from 'graphql-yoga' -- it is the standard graphql-js export)
(You MUST prefer Yoga-specific plugins over Envelop equivalents -- Yoga plugins operate at the HTTP layer and can skip GraphQL execution entirely for cached/persisted results)
(You MUST use createSchema from 'graphql-yoga' for schema-first -- passing raw typeDefs/resolvers objects directly to createYoga is not supported in v5)
(You MUST use named constants for all numeric values -- timeouts, TTLs, port numbers, limits)
</critical_requirements>
Auto-detection: GraphQL Yoga, graphql-yoga, createYoga, createSchema, createPubSub, Envelop, useResponseCache, useCSRFPrevention, usePersistedOperations, GraphQL subscriptions SSE, error masking, maskedErrors, graphql-ws, Yoga plugin hooks, onRequest, onParams
When to use:
When NOT to use:
Key patterns covered:
createYoga and createSchema (schema-first)createYoga<ServerContext>graphql-ws, built-in PubSubGraphQLError exposureFile scalarDetailed Resources:
GraphQL Yoga is a batteries-included, Fetch API-compatible GraphQL server. Its core is built on the WHATWG Fetch API (Request/Response), making it runtime-agnostic -- the same server code deploys to Node.js, Bun, Deno, and edge runtimes. The Envelop plugin system provides composable middleware at both the HTTP and GraphQL execution layers.
Schema approach: Yoga is schema-library agnostic. Use createSchema (schema-first SDL), Pothos (code-first), or vanilla graphql-js -- anything that produces a GraphQLSchema works.
Plugin priority: When both an Envelop plugin and a Yoga-specific plugin exist for the same feature (caching, persisted operations, defer/stream), always choose the Yoga variant. Yoga plugins hook into the HTTP layer and can short-circuit before GraphQL execution begins, skipping parsing and validation entirely for cached or persisted results.
Error philosophy: All unexpected errors are masked by default in production. Intentional errors are thrown as GraphQLError from the graphql package -- these bypass masking and reach clients with their message and extensions intact.
Create a Yoga instance with createSchema for SDL-based schemas. The yoga instance IS a Fetch API handler -- pass it directly to any runtime's HTTP server.
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`);
});
Why good: createSchema wraps makeExecutableSchema, yoga instance is a standard Fetch handler, works on any runtime
See examples/core.md for complete setup, cross-runtime deployment, and type-safe context.
Pass a generic to createYoga for server-specific context typing. The context factory receives YogaInitialContext (containing request and params) and returns your custom context.
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 };
},
});
Why good: generic types flow to resolver context parameter, request uses standard Fetch API (not framework-specific req/res)
See examples/core.md for full context patterns.
Plugins are passed in the plugins array. Use Yoga-specific plugins when available -- they operate at the HTTP layer and can skip GraphQL execution entirely.
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,
}),
],
});
Why good: Yoga response cache skips parsing/validation for cached results (Envelop equivalent cannot), plugins compose without conflicts
See examples/plugins.md for custom plugins, lifecycle hooks, and all Yoga-specific plugins.
Yoga uses Server-Sent Events by default for subscriptions -- no WebSocket setup needed. Use AsyncGenerator syntax in subscription resolvers.
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 };
}
},
},
},
},
});
Why good: no WebSocket infrastructure needed, works through HTTP proxies and load balancers, graphql-sse library for clients
See examples/subscriptions.md for PubSub, WebSocket setup, and filtering.
Yoga masks all unexpected errors by default. Throw GraphQLError (from graphql) for intentional client-facing errors -- these bypass masking.
import { GraphQLError } from "graphql";
const NOT_FOUND_CODE = "USER_NOT_FOUND";
throw new GraphQLError("User not found", {
extensions: { code: NOT_FOUND_CODE },
});
Why good: unexpected errors never leak internals (database details, stack traces), intentional errors pass through with message + extensions
See examples/error-handling.md for custom masking, disabling masking, and development mode.
Yoga supports the GraphQL Multipart Request spec. Add a File scalar and receive WHATWG File objects in resolvers.
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();
// Process file content
return true;
},
},
},
});
Why good: uses standard WHATWG File API (same as browser), no extra packages needed, disable with multipart: false
See examples/core.md for complete file upload patterns.
</patterns><decision_framework>
Need auto-generated types from SDL?
+-- YES --> createSchema (schema-first with typeDefs + resolvers)
+-- NO --> Want full TypeScript inference in schema definition?
+-- YES --> Code-first library (e.g. Pothos) -- pass resulting GraphQLSchema to Yoga
+-- NO --> Vanilla graphql-js GraphQLSchema
Need subscriptions?
+-- YES --> Do clients need bidirectional communication?
| +-- YES --> WebSocket via graphql-ws (add ws + graphql-ws packages)
| +-- NO --> SSE (default, zero config, works through proxies)
+-- NO --> No subscription setup needed
Feature available as Yoga-specific plugin?
+-- YES --> Use Yoga plugin (HTTP-level hooks, can skip execution)
+-- NO --> Use Envelop plugin (GraphQL execution-level hooks)
| Plugin | Package | Why Yoga-specific |
|--------|---------|-------------------|
| Response Cache | @graphql-yoga/plugin-response-cache | Skips execution for cached queries |
| Persisted Operations | @graphql-yoga/plugin-persisted-operations | Rejects unknown operations at HTTP layer |
| Defer/Stream | @graphql-yoga/plugin-defer-stream | Streams via HTTP chunked encoding |
| CSRF Prevention | @graphql-yoga/plugin-csrf-prevention | Requires custom header before parsing |
| GraphQL SSE | @graphql-yoga/plugin-graphql-sse | Single-connection SSE mode |
</decision_framework>
<red_flags>
High Priority:
GraphQLError from graphql-yoga instead of graphql -- wrong package, will failtypeDefs/resolvers object directly to createYoga without createSchema -- not supported in v5Error in resolvers expecting clients to see the message -- masked to "Unexpected error." in productionMedium Priority:
*, which should be locked downcreateRedisEventTarget)graphql peer dependency -- graphql-yoga requires graphql as a peer, install bothcreateSchema with no schema at all -- Yoga requires a schema; it does not infer oneGotchas & Edge Cases:
YogaInitialContext.request is a Fetch API Request, not a Node.js IncomingMessage -- use request.headers.get(), not req.headersaddPlugin in onPluginInit now execute immediately after the adding plugin, not lastuseResponseCache session callback must return a string (user ID) for PRIVATE scope or null for public -- returning undefined breaks cachingX-Accel-Buffering: no for NginxFile scalar in uploads gives you a WHATWG File object -- use .text(), .arrayBuffer(), or .stream() methods (not Node.js Buffer directly)graphiql: false in productionmaskedErrors set to false disables ALL masking including stack traces -- use custom maskError function instead for selective exposurecredentials: true with origin: '*' is rejected by browsers per the Fetch spec -- specify exact origins</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST import GraphQLError from 'graphql', NOT from 'graphql-yoga' -- it is the standard graphql-js export)
(You MUST prefer Yoga-specific plugins over Envelop equivalents -- Yoga plugins operate at the HTTP layer and can skip GraphQL execution entirely for cached/persisted results)
(You MUST use createSchema from 'graphql-yoga' for schema-first -- passing raw typeDefs/resolvers objects directly to createYoga is not supported in v5)
(You MUST use named constants for all numeric values -- timeouts, TTLs, port numbers, limits)
Failure to follow these rules will cause import errors, missed performance optimizations, and information leakage through unmasked errors.
</critical_reminders>
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
tools
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events