Skip to main content
The base GraphQL client provides low-level GraphQL functionality used by both Admin and Storefront API clients.

createGraphQLClient()

Creates a generic GraphQL client with support for queries, mutations, and streaming responses.
import { createGraphQLClient } from '@shopify/graphql-client';

const client = createGraphQLClient({
  url: 'https://my-store.myshopify.com/api/2025-10/graphql.json',
  headers: {
    'Content-Type': 'application/json',
    'X-Shopify-Access-Token': 'your-access-token',
  },
});
In most cases, you should use createAdminApiClient() or createStorefrontApiClient() instead of the base GraphQL client. Those clients handle authentication, URL formatting, and API versioning automatically.

Parameters

url
string
required
The complete GraphQL API endpoint URL
headers
Headers
required
HTTP headers to include with every request. Must include authentication headers.
headers: {
  'Content-Type': 'application/json',
  'X-Shopify-Access-Token': 'token',
}
retries
number
default:"0"
Number of retry attempts for failed requests
customFetchApi
CustomFetchApi
default:"fetch"
Custom fetch implementation. Useful for adding middleware or using in non-browser environments.
type CustomFetchApi = (
  url: string,
  init?: {
    method?: string;
    headers?: HeadersInit;
    body?: string;
    signal?: AbortSignal;
    keepalive?: boolean;
  }
) => Promise<Response>;
logger
Logger
Logger function for debugging and monitoring. Receives log events for HTTP responses, retries, and deprecation notices.
type Logger = (logContent: LogContentTypes) => void;

Returns

GraphQLClient
object
GraphQL client instance with the following properties:

Client Methods

fetch()

Executes a GraphQL operation and returns the raw Response object.
const response = await client.fetch(
  `query { shop { name } }`,
  {
    variables: {},
    headers: { 'X-Custom': 'value' },
  }
);

const data = await response.json();
operation
string
required
GraphQL query or mutation string
options
RequestOptions
Request options:

request()

Executes a GraphQL operation and returns parsed response data with error handling.
const { data, errors, extensions } = await client.request(
  `query getShop {
    shop {
      name
      email
    }
  }`
);

if (errors) {
  console.error('GraphQL errors:', errors.graphQLErrors);
} else {
  console.log('Shop:', data.shop.name);
}
Do not use request() for operations with @defer directives. Use requestStream() instead.
Returns: Promise<ClientResponse<TData>>
data
TData
The GraphQL response data
errors
ResponseErrors
Error information if the request failed:
extensions
object
GraphQL extensions from the response (e.g., query cost information)
headers
Headers
Response headers

requestStream()

Executes a GraphQL operation with @defer directives and returns an async iterator for streaming responses.
const stream = await client.requestStream(
  `query {
    shop { name }
    products(first: 100) @defer {
      edges { node { id title } }
    }
  }`
);

for await (const chunk of stream) {
  console.log('Received chunk:', chunk.data);
  console.log('Has more data:', chunk.hasNext);
  
  if (chunk.errors) {
    console.error('Chunk errors:', chunk.errors);
  }
}
Only use requestStream() for operations containing @defer directives. For regular operations, use request() instead.
Returns: Promise<ClientStreamIterator<TData>> The async iterator yields ClientStreamResponse<TData> objects:
data
TData
Accumulated GraphQL response data from all chunks received so far
hasNext
boolean
true if more data chunks are expected, false when the stream is complete
errors
ResponseErrors
Error information if the request or stream failed
extensions
object
GraphQL extensions from the response

Example Usage

import { createGraphQLClient } from '@shopify/graphql-client';

const client = createGraphQLClient({
  url: 'https://my-store.myshopify.com/admin/api/2025-10/graphql.json',
  headers: {
    'Content-Type': 'application/json',
    'X-Shopify-Access-Token': process.env.ACCESS_TOKEN,
  },
  retries: 2,
});

Types

interface ClientOptions {
  headers: Record<string, string | string[]>;
  url: string;
  customFetchApi?: CustomFetchApi;
  retries?: number;
  logger?: Logger;
}

interface ClientConfig {
  readonly headers: Record<string, string | string[]>;
  readonly url: string;
  readonly retries: number;
}

interface RequestOptions {
  variables?: Record<string, any>;
  url?: string;
  headers?: Record<string, string | string[]>;
  retries?: number;
  keepalive?: boolean;
  signal?: AbortSignal;
}

interface ClientResponse<TData = any> {
  data?: TData;
  errors?: ResponseErrors;
  extensions?: Record<string, any>;
  headers?: Headers;
}

interface ClientStreamResponse<TData = any> extends ClientResponse<TData> {
  hasNext: boolean;
}

interface ResponseErrors {
  networkStatusCode?: number;
  message?: string;
  graphQLErrors?: any[];
  response?: Response;
}

type Logger = (logContent: LogContentTypes) => void;

type LogContentTypes =
  | HTTPResponseLog
  | HTTPRetryLog
  | HTTPResponseGraphQLDeprecationNotice;

Retry Behavior

The client automatically retries requests for certain HTTP status codes:
  • 429 - Too Many Requests (rate limiting)
  • 503 - Service Unavailable
Retries use exponential backoff with the following behavior:
  • First retry: immediate
  • Subsequent retries: wait time increases exponentially
  • Retry-After header is respected when present
const client = createGraphQLClient({
  url: API_URL,
  headers: HEADERS,
  retries: 3, // Retry up to 3 times
});

Logging

The logger receives three types of events:

HTTP-Response

{
  type: 'HTTP-Response',
  content: {
    requestParams: [url, init],
    response: Response,
  }
}

HTTP-Retry

{
  type: 'HTTP-Retry',
  content: {
    requestParams: [url, init],
    lastResponse?: Response,
    retryAttempt: number,
    maxRetries: number,
  }
}

HTTP-Response-GraphQL-Deprecation-Notice

{
  type: 'HTTP-Response-GraphQL-Deprecation-Notice',
  content: {
    requestParams: [url, init],
    deprecationNotice: string,
  }
}

Build docs developers (and LLMs) love