ProvenPin Install

GraphQL Yoga 5 schema, context and errors tested via fetch: working versions (tested 2026-10-15)

Build a GraphQL Yoga server with SDL resolvers, per-request context auth and GraphQLError codes, tested in-process with yoga.fetch.

passed4 passed · 0 failed2026-10-15 13:30 UTC
Status
passed
Last passed
2026-10-15 13:30 UTC
Tests
4 / 4
Language
TypeScript
Runtime
node v22.23.3
pinned versions4
graphql-yoga@5.24.5graphql@17.0.2tsx@4.23.15@types/node@24.19.2
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38742087302

The code

Exactly the files that passed. Your agent can pull them with get_recipe("graphql-yoga-schema-context") (install), or see them on GitHub.

package.json
{
  "name": "graphql-yoga-schema-context",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "graphql-yoga": "5.24.5",
    "graphql": "17.0.2"
  },
  "devDependencies": {
    "tsx": "4.23.15",
    "@types/node": "24.19.2"
  }
}
src/yoga.ts
import { createSchema, createYoga } from "graphql-yoga";
import { GraphQLError } from "graphql";

type Ctx = { userId: string | null };
const books = [{ id: "1", title: "SICP", ownerId: "ada" }];

export const yoga = createYoga<{}, Ctx>({
  context: ({ request }) => ({ userId: request.headers.get("x-user-id") }),
  schema: createSchema<Ctx>({
    typeDefs: /* GraphQL */ `
      type Book { id: ID!, title: String! }
      type Query { books: [Book!]!, myBooks: [Book!]! }
      type Mutation { addBook(title: String!): Book! }
    `,
    resolvers: {
      Query: {
        books: () => books,
        myBooks: (_p, _a, ctx) => {
          if (!ctx.userId) throw new GraphQLError("Sign in first", { extensions: { code: "UNAUTHENTICATED" } });
          return books.filter((b) => b.ownerId === ctx.userId);
        },
      },
      Mutation: {
        addBook: (_p, { title }: { title: string }, ctx) => {
          if (!ctx.userId) throw new GraphQLError("Sign in first", { extensions: { code: "UNAUTHENTICATED" } });
          const b = { id: String(books.length + 1), title, ownerId: ctx.userId };
          books.push(b);
          return b;
        },
      },
    },
  }),
  maskedErrors: true, // unexpected errors are hidden; GraphQLError messages pass through
});
test/yoga.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { yoga } from "../src/yoga.ts";

async function gql(query: string, user?: string, variables?: object) {
  const res = await yoga.fetch("http://yoga/graphql", { method: "POST", headers: { "content-type": "application/json", ...(user ? { "x-user-id": user } : {}) }, body: JSON.stringify({ query, variables }) });
  return res.json();
}

test("public query", async () => {
  assert.deepEqual((await gql("{ books { title } }")).data.books[0], { title: "SICP" });
});

test("auth error carries a code", async () => {
  const r = await gql("{ myBooks { id } }");
  assert.equal(r.errors[0].extensions.code, "UNAUTHENTICATED");
});

test("mutation with variables and context", async () => {
  const r = await gql("mutation($t: String!) { addBook(title: $t) { id title } }", "bob", { t: "TAOCP" });
  assert.equal(r.data.addBook.title, "TAOCP");
  assert.equal((await gql("{ myBooks { title } }", "bob")).data.myBooks.length, 1);
});

test("validation errors for bad queries", async () => {
  assert.ok((await gql("{ nope }")).errors.length);
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.