ProvenPin Install

graphql-request client with typed operations, mocked by MSW graphql handlers: working versions (tested 2026-10-15)

Call a GraphQL API with graphql-request (variables, headers, error handling) and test offline with MSW's graphql.link(url).query/mutation handlers (msw 3 API). graphql stays on 16.x: graphql-request 7 peers on graphql 14 - 16.

passed3 passed · 0 failed2026-10-15 13:22 UTC
Status
passed
Last passed
2026-10-15 13:22 UTC
Tests
3 / 3
Language
TypeScript
Runtime
node v22.23.3
pinned versions5
graphql-request@7.4.0graphql@16.14.2tsx@4.23.15@types/node@24.19.2msw@3.0.3
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 38741369695

The code

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

package.json
{
  "name": "graphql-request-msw",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "graphql-request": "7.4.0",
    "graphql": "16.14.2"
  },
  "devDependencies": {
    "tsx": "4.23.15",
    "@types/node": "24.19.2",
    "msw": "3.0.3"
  }
}
src/api.ts
import { GraphQLClient, gql, ClientError } from "graphql-request";

export const client = (url: string, token: string) => new GraphQLClient(url, { headers: { authorization: `Bearer ${token}` } });

const GetRepo = gql`query GetRepo($name: String!) { repo(name: $name) { name stars } }`;
const Star = gql`mutation Star($name: String!) { star(name: $name) { name stars } }`;
type Repo = { name: string; stars: number };

export const getRepo = async (c: GraphQLClient, name: string) => (await c.request<{ repo: Repo | null }>(GetRepo, { name })).repo;
export const star = async (c: GraphQLClient, name: string) => (await c.request<{ star: Repo }>(Star, { name })).star;

export function errorCode(e: unknown) {
  return e instanceof ClientError ? (e.response.errors?.[0]?.extensions as any)?.code ?? "UNKNOWN" : "NETWORK";
}
test/api.test.ts
import { test, before, after, afterEach } from "node:test";
import assert from "node:assert/strict";
import { HttpResponse } from "msw";
import { graphql } from "msw/graphql"; // msw 3 moved graphql handlers to a subpath
import { setupServer } from "msw/node";
import { client, errorCode, getRepo, star } from "../src/api.ts";

const URL_ = "https://api.example.com/graphql";
const repos: Record<string, number> = { provenpin: 5 };
const api = graphql.link(URL_); // msw 3: handlers are scoped to an endpoint
const server = setupServer(
  api.query("GetRepo", ({ variables, request }) => {
    if (request.headers.get("authorization") !== "Bearer t") return HttpResponse.json({ errors: [{ message: "nope", extensions: { code: "UNAUTHENTICATED" } }] });
    const stars = repos[variables.name as string];
    return HttpResponse.json({ data: { repo: stars === undefined ? null : { name: variables.name, stars } } });
  }),
  api.mutation("Star", ({ variables }) => HttpResponse.json({ data: { star: { name: variables.name, stars: ++repos[variables.name as string] } } })),
);
before(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
after(() => server.close());

test("query with variables", async () => {
  assert.deepEqual(await getRepo(client(URL_, "t"), "provenpin"), { name: "provenpin", stars: 5 });
  assert.equal(await getRepo(client(URL_, "t"), "missing"), null);
});

test("mutation", async () => {
  assert.equal((await star(client(URL_, "t"), "provenpin")).stars, 6);
});

test("GraphQL errors surface with their code", async () => {
  const e = await getRepo(client(URL_, "bad"), "provenpin").catch((x) => x);
  assert.equal(errorCode(e), "UNAUTHENTICATED");
});

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.