iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

graphql-request

graphql-request is the smallest sensible GraphQL client: ~7KB, no cache, no React bindings. Use it for scripts, server-to-server calls, edge functions, and any place a normalised cache is overkill. Pair with codegen for full type safety.

Typed queries, mutations, retries, file upload

EXAMPLE
// npm i graphql-request graphql
import { GraphQLClient, gql } from 'graphql-request';

// 1) Create a client
const client = new GraphQLClient('https://api.example.com/graphql', {
  headers: () => ({ authorization: 'Bearer ' + getToken() }),   // computed per-request
  fetch: globalThis.fetch,                                       // override (e.g. with undici)
  timeout: 10_000,
  errorPolicy: 'all',                                            // include partial data even on errors
});

// 2) Typed query — using a manual interface for now; combine with codegen below
type Order = { id: string; customer: string; totalCents: number; status: string };
type OrdersResp = { orders: { data: Order[]; nextCursor: string | null } };

const ORDERS = gql\`
  query Orders($status: OrderStatus, $after: String) {
    orders(status: $status, after: $after) {
      data { id customer totalCents status }
      nextCursor
    }
  }
\`;

const data = await client.request<OrdersResp>(ORDERS, { status: 'OPEN', after: null });

// 3) Mutation
const CANCEL = gql\`
  mutation Cancel($id: ID!) { cancelOrder(id: $id) { id status } }
\`;
await client.request(CANCEL, { id: 'o1' });

// 4) File upload (multipart) — supported via the @0no-co/graphql.web GraphQLClient.uploadFile pattern
// const { request } = require('graphql-request');
// const file = new File(['hello'], 'hello.txt');
// await client.request(UPLOAD, { file });
// The server must implement the GraphQL multipart spec.

// 5) Retries + backoff (BYO — keep it small)
async function withRetries<T>(fn: () => Promise<T>, max = 3): Promise<T> {
  let last: unknown;
  for (let i = 0; i < max; i++) {
    try { return await fn(); }
    catch (e) {
      last = e;
      const status = (e as any).response?.status ?? 0;
      if (status >= 400 && status < 500) throw e;       // do not retry client errors
      await new Promise((r) => setTimeout(r, 250 * 2 ** i));
    }
  }
  throw last;
}

const result = await withRetries(() => client.request<OrdersResp>(ORDERS, { status: 'OPEN' }));

// 6) Streaming via batched requests (request batching)
// graphql-request does NOT batch; if you need it use urql / Apollo or a server-side proxy.

// 7) Error handling — request() throws a ClientError with .response
try {
  await client.request(ORDERS);
} catch (e: any) {
  console.error('status', e.response?.status, 'errors', e.response?.errors);
  // Partial data when errorPolicy is 'all':
  console.log('partial data', e.response?.data);
}

// 8) Codegen for full type safety
// .graphqlrc.yml + @graphql-codegen/cli
// Generates typed wrappers like 'ordersQuery(client, variables)' so request() inference is automatic.

// 9) Where graphql-request shines
// - server-to-server scripts and cron jobs
// - Cloudflare Workers / Vercel Edge / Deno
// - test suites that hit the API
// - very small frontend libraries with their own cache
// Where it does not:
// - normalised caching across components (use urql / Apollo)
// - subscriptions (use a WS-aware client)
// - very large query graphs with shared fragments (Relay)

// 10) Pitfalls
// - Forgetting to pass headers per-request (e.g. token refresh) — use the
//   'headers: () => ...' callback form
// - Catching errors without inspecting e.response.errors (partial data hides there)
// - Reusing one client for multiple tenants with different auth (build per-request)
function getToken() { return 'token-here'; }

Why it matters

Use graphql-request anywhere a normalised client cache is overkill — scripts, edge functions, server-to-server calls. Pair it with codegen for compile-time-checked queries and the result is a tiny, fast, fully typed GraphQL client that fits in a Cloudflare Worker without spending half of the bundle budget on the client itself.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { request, gql } from 'graphql-request';
const data = await request('/graphql', gql\`{ users { id name } }\`);
Try it Yourself »

Discussion

Loading…