Jak używać
- Skopiuj polecenie instalacji i sprawdź źródła przed instalacją we własnym środowisku agenta.
- W oryginale sprawdź wymagane narzędzia, dane dostępowe i obsługiwane środowiska.
- Opisz dane wejściowe i oczekiwany rezultat; porównaj wynik ze źródłami.
npx skills add https://github.com/TerminalSkills/skills --skill graphql-yogaPrzed rozpoczęciem
- Instalacja nie obejmuje subskrypcji modelu, limitu API ani uprawnień do plików.
- Gwiazdki i instalacje pokazują aktywność, a nie niezależny test jakości lub bezpieczeństwa.
Treść z repozytorium z zachowaniem nagłówków, przykładów, kodu, tabel, linków i obrazów.
GraphQL Yoga
Overview
GraphQL Yoga (v5) is a spec-compliant GraphQL server built on the WHATWG Fetch API, so the same yoga object works as a Node http handler, a Fetch handler (yoga.fetch) in Workers, Bun and Deno, or behind Express, Fastify and Next.js. It is extended with Envelop plugins. It ships with GraphQL over SSE subscriptions, multipart file uploads, CORS, GraphiQL and error masking; you add caching, rate limiting and depth limits as plugins. Schemas can be written with createSchema (SDL plus resolvers) or with code-first builders such as Pothos.
Instructions
- Install:
npm install graphql-yoga graphql. - Build the schema with
createSchema({ typeDefs, resolvers }), pass it tocreateYoga, and hand the result tocreateServerfromnode:http. The endpoint defaults to/graphql; change it withgraphqlEndpoint. - Per-request state (database handle, current user) goes in the
contextoption, which receives{ request }(a FetchRequest) plus runtime-specific fields such asreq/reson Node. - Subscriptions: use
createPubSub()exported bygraphql-yoga;pubSub.publish(topic, payload)andpubSub.subscribe(topic)(an async iterable). Clients connect over SSE by default; no WebSocket server is needed. For several instances, back the pub/sub with Redis using@graphql-yoga/redis-event-target. - File uploads: declare
scalar Filein the schema and acceptFile!arguments. The value is a standardFile/Blob(.text(),.arrayBuffer(),.stream()). Multipart parsing is on by default;multipart: falseturns it off. - Errors: Yoga masks unexpected errors by default. Throw
GraphQLError(fromgraphql) withextensions.codefor errors clients should see.maskedErrors: falsedisables masking (development only). WithNODE_ENV=developmentthe original error appears inextensions.originalError. - GraphiQL is enabled only in development by default; set
graphiql: falseor a function to control it. - Plugins go in
plugins: []. Packages verified to exist:
@graphql-yoga/plugin-response-cacheexportsuseResponseCache({ session, ttl, invalidateViaMutation });ttlis milliseconds,sessionreturns a string per user ornullfor a global cache.@envelop/rate-limiterexportsuseRateLimiter({ identifyFn }); limits are declared in the schema with the@rateLimit(max: 10, window: "5s")directive (you must define the directive in your SDL).@envelop/depth-limitexportsuseDepthLimit({ maxDepth }).@escape.tech/graphql-armor-max-depthis an alternative.
Examples
Example 1: Users API with SSE subscription and masked errors
Request: "Build a GraphQL API on port 4000 where clients can list users and get notified when one is created."
// server.ts
import { createServer } from "node:http";
import { GraphQLError } from "graphql";
import { createPubSub, createSchema, createYoga } from "graphql-yoga";
const pubSub = createPubSub<{ userCreated: [user: { id: string; name: string }] }>();
const users = [{ id: "1", name: "Maria Lopez" }];
const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */ `
type User { id: ID! name: String! }
type Query { users: [User!]! user(id: ID!): User }
type Mutation { createUser(name: String!): User! }
type Subscription { userCreated: User! }
`,
resolvers: {
Query: {
users: () => users,
user: (_, { id }) => {
const found = users.find((u) => u.id === id);
if (!found) throw new GraphQLError(`User ${id} not found`, { extensions: { code: "NOT_FOUND" } });
return found;
},
},
Mutation: {
createUser: (_, { name }) => {
const user = { id: String(users.length + 1), name };
users.push(user);
pubSub.publish("userCreated", user);
return user;
},
},
Subscription: {
userCreated: { subscribe: () => pubSub.subscribe("userCreated"), resolve: (u) => u },
},
},
}),
cors: { origin: ["https://app.northwind.dev"], credentials: true },
});
createServer(yoga).listen(4000, () => console.log("http://localhost:4000/graphql"));Run with npx tsx server.ts. Opening http://localhost:4000/graphql shows GraphiQL; { user(id: "9") { name } } returns the NOT_FOUND error message, while a thrown plain Error comes back as "Unexpected error.".
Example 2: Cache, depth limit and a test without a network
Request: "Cache public queries for a minute and reject queries nested deeper than 8 levels."
npm install @graphql-yoga/plugin-response-cache @envelop/depth-limitimport { createSchema, createYoga } from "graphql-yoga";
import { useResponseCache } from "@graphql-yoga/plugin-response-cache";
import { useDepthLimit } from "@envelop/depth-limit";
export const yoga = createYoga({
schema: createSchema({ typeDefs, resolvers }),
plugins: [
useResponseCache({
session: (request) => request.headers.get("authorization"),
ttl: 60_000,
}),
useDepthLimit({ maxDepth: 8 }),
],
});
// yoga.fetch needs no running server, so it works in unit tests:
const res = await yoga.fetch("http://localhost/graphql", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ query: "{ users { id } }" }),
});
console.log(await res.json()); // { data: { users: [...] } }A query deeper than the limit returns HTTP 200 with errors[0].extensions.code of GRAPHQL_VALIDATION_FAILED and a message such as "exceeds maximum operation depth of 8".
Guidelines
- The old
useRateLimiterimport from@graphql-yoga/plugin-rate-limiterand theenvelop-depth-limitpackage do not exist; use the@envelop/*packages above. - Keep GraphiQL off in production and keep masking on; expose failures deliberately via
GraphQLError. - Use DataLoader (one instance per request, created in
context) to avoid N+1 queries in nested resolvers. - An in-memory response cache and in-memory PubSub work for a single process only; use a shared cache and the Redis event target when you run several replicas.
- Cache by
sessionfor authenticated data, otherwise one user's response can be served to another. - Depth limiting alone does not stop expensive wide queries; add query complexity limits, persisted operations or timeouts for public APIs.
- Check the migration guide on the-guild.dev before upgrading from v3/v4 to v5.

