@shopify/graphql-client
Version:
Shopify GraphQL Client - A lightweight generic GraphQL JS client to interact with Shopify GraphQL APIs
331 lines (260 loc) • 17.9 kB
Markdown
# GraphQL Client
The GraphQL Client can be used to interact with any Shopify's GraphQL APIs. Client users are expected to provide the full API URL and necessary headers.
## Getting Started
Using your preferred package manager, install this package in a project:
```sh
yarn add @shopify/graphql-client
```
```sh
npm install @shopify/graphql-client --s
```
```sh
pnpm add @shopify/graphql-client
```
### CDN
The UMD builds of each release version are available via the [`unpkg` CDN](https://unpkg.com/browse/@shopify/graphql-client@latest/dist/umd/)
```html
// The minified `v0.9.3` version of the GraphQL API Client
<script src="https://unpkg.com/@shopify/graphql-client@0.9.3/dist/umd/graphql-client.min.js"></script>
<script>
const client = ShopifyGraphQLClient.createGraphQLClient({...});
</script>
```
## Client initialization
```typescript
import {createGraphQLClient} from '@shopify/graphql-client';
const client = createGraphQLClient({
url: 'http://your-shop-name.myshopify.com/api/2023-10/graphql.json',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': 'public-token',
},
retries: 1
});
```
### Create a server enabled client using a custom Fetch API
In order to use the client within a server, a server enabled JS Fetch API will need to be provided to the client at initialization. By default, the client uses `window.fetch` for network requests.
```typescript
import {createGraphQLClient} from '@shopify/graphql-client';
import {fetch as nodeFetch} from 'node-fetch';
const client = createGraphQLClient({
url: 'http://your-shop-name.myshopify.com/api/2023-10/graphql.json',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': 'public-token',
},
customFetchApi: nodeFetch
});
```
### `createGraphQLClient()` parameters
| Property | Type | Description |
| -------- | ------------------------ | ---------------------------------- |
| url | `string` | The GraphQL API URL |
| headers | `Record<string, string \| string[]>` | Headers to be included in requests |
| retries? | `number` | The number of HTTP request retries if the request was abandoned or the server responded with a `Too Many Requests (429)` or `Service Unavailable (503)` response. Default value is `0`. Maximum value is `3`. |
| customFetchApi? | `(url: string, init?: {method?: string, headers?: HeaderInit, body?: string}) => Promise<Response>` | A replacement `fetch` function that will be used in all client network requests. By default, the client uses `window.fetch()`. |
| logger? | `(logContent: `[`HTTPResponseLog`](#httpresponselog)`\|`[`HTTPRetryLog`](#httpretrylog)`\|`[`HTTPResponseGraphQLDeprecationNotice`](#httpresponsegraphqldeprecationnotice)`) => void` | A logger function that accepts [log content objects](#log-content-types). This logger will be called in certain conditions with contextual information. |
## Client properties
| Property | Type | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| config | `{url: string, headers: Record<string, string \| string[]>, retries: number}` | Configuration for the client |
| fetch | `(operation: string, options?: `[`RequestOptions`](#requestoptions-properties)`) => Promise<Response>` | Fetches data from the GraphQL API using the provided GQL operation string and [`RequestOptions`](#requestoptions-properties) object and returns the network response |
| request | `<TData>(operation: string, options?: `[`RequestOptions`](#requestoptions-properties)`) => Promise<`[`ClientResponse<TData>`](#ClientResponsetdata)`>` | Fetches data from the GraphQL API using the provided GQL operation string and [`RequestOptions`](#requestoptions-properties) object and returns a [normalized response object](#clientresponsetdata) |
| requestStream | `<TData>(operation: string, options?: `[`RequestOptions`](#requestoptions-properties)`) => Promise <AsyncIterator<`[`ClientStreamResponse<TData>`](#clientstreamresponsetdata)`>>` | Fetches GQL operations that can result in a streamed response from the API (eg. [Storefront API's `@defer` directive](https://shopify.dev/docs/api/storefront#directives)). The function returns an async iterator and the iterator will return [normalized stream response objects](#clientstreamresponsetdata) as data becomes available through the stream. |
## `RequestOptions` properties
| Name | Type | Description |
| ---------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| variables? | `Record<string, any>` | Variable values needed in the graphQL operation |
| url? | `string` | Alternative request API URL |
| headers? | `Record<string, string \| string[]>` | Additional and/or replacement headers to be used in the request |
| retries? | `number` | Alternative number of retries for the request. Retries only occur for requests that were abandoned or if the server responds with a `Too Many Request (429)` or `Service Unavailable (503)` response. Minimum value is `0` and maximum value is `3`. |
| keepalive? | `boolean` | Whether to keep a connection alive when page is unloaded before a request has completed. Default value is `false`. |
| signal? | `AbortSignal` | If this option is set, the request can be canceled by calling `abort()` on the corresponding `AbortController`. |
## `ClientResponse<TData>`
| Name | Type | Description |
| ----------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| data? | `TData \| any` | Data returned from the GraphQL API. If `TData` was provided to the function, the return type is `TData`, else it returns type `any`. |
| errors? | [`ResponseErrors`](#responseerrors) | Errors object that contains any API or network errors that occured while fetching the data from the API. It does not include any `UserErrors`. |
| extensions? | `Record<string, any>` | Additional information on the GraphQL response data and context. It can include the `context` object that contains the context settings used to generate the returned API response. |
## `ClientStreamResponse<TData>`
| Name | Type | Description |
| ----------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| data? | `TData \| any` | Currently available data returned from the GraphQL API. If `TData` was provided to the function, the return type is `TData`, else it returns type `any`. |
| errors? | [`ResponseErrors`](#responseerrors) | Errors object that contains any API or network errors that occured while fetching the data from the API. It does not include any `UserErrors`. |
| extensions? | `Record<string, any>` | Additional information on the GraphQL response data and context. It can include the `context` object that contains the context settings used to generate the returned API response. |
| hasNext | `boolean` | Flag to indicate whether the response stream has more incoming data |
## `ResponseErrors`
| Name | Type | Description |
| ----------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| networkStatusCode? | `number` | HTTP response status code |
| message? | `string` | The provided error message |
| graphQLErrors? | `any[]` | The GraphQL API errors returned by the server |
| response? | `Response` | The raw response object from the network fetch call |
## Usage examples
### Query for a product
```typescript
const productQuery = `
query ProductQuery($handle: String) {
product(handle: $handle) {
id
title
handle
}
}
`;
const {data, errors, extensions} = await client.request(productQuery, {
variables: {
handle: 'sample-product',
},
});
```
### Query for product info using the `@defer` directive
```typescript
const productQuery = `
query ProductQuery($handle: String) {
product(handle: $handle) {
id
handle
... @defer(label: "deferredFields") {
title
description
}
}
}
`;
const responseStream = await client.requestStream(productQuery, {
variables: {handle: 'sample-product'},
});
// await available data from the async iterator
for await (const response of responseStream) {
const {data, errors, extensions, hasNext} = response;
}
```
### Add additional custom headers to the API request
```typescript
const productQuery = `
query ProductQuery($handle: String) {
product(handle: $handle) {
id
title
handle
}
}
`;
const {data, errors, extensions} = await client.request(productQuery, {
variables: {
handle: 'sample-product',
},
headers: {
'Shopify-Storefront-Id': 'shop-id',
},
});
```
### Use an updated API URL in the API request
```typescript
const productQuery = `
query ProductQuery($handle: String) {
product(handle: $handle) {
id
title
handle
}
}
`;
const {data, errors, extensions} = await client.request(productQuery, {
variables: {
handle: 'sample-product',
},
url: 'http://your-shop-name.myshopify.com/api/unstable/graphql.json',
});
```
### Set a custom retries value in the API request
```typescript
const shopQuery = `
query ShopQuery {
shop {
name
id
}
}
`;
// Will retry the HTTP request to the server 2 times if the requests were abandoned or the server responded with a 429 or 503 error
const {data, errors, extensions} = await client.request(shopQuery, {
retries: 2,
});
```
### Make a request that should run even if page is unloaded
```typescript
const shopQuery = `
query ShopQuery {
shop {
name
id
}
}
`;
const {data, errors, extensions} = await client.request(shopQuery, {
keepalive: true,
});
```
### Provide GQL query type to `client.request()` and `client.requestStream()`
```typescript
import {print} from 'graphql/language';
// GQL operation types are usually auto generated during the application build
import {CollectionQuery, CollectionDeferredQuery} from 'types/appTypes';
import collectionQuery from './collectionQuery.graphql';
import collectionDeferredQuery from './collectionDeferredQuery.graphql';
const {data, errors, extensions} = await client.request<CollectionQuery>(
print(collectionQuery),
{
variables: {
handle: 'sample-collection',
},
}
);
const responseStream = await client.requestStream<CollectionDeferredQuery>(
print(collectionDeferredQuery),
{
variables: {handle: 'sample-collection'},
}
);
```
### Using `client.fetch()` to get API data
```typescript
const shopQuery = `
query shop {
shop {
name
id
}
}
`;
const response = await client.fetch(shopQuery);
if (response.ok) {
const {errors, data, extensions} = await response.json();
}
```
## Log Content Types
### `HTTPResponseLog`
This log content is sent to the logger whenever a HTTP response is received by the client.
| Property | Type | Description |
| -------- | ------------------------ | ---------------------------------- |
| type | `LogType['HTTP-Response']` | The type of log content. Is always set to `HTTP-Response` |
| content | `{`[`requestParams`](#requestparams)`: [url, init?], response: Response}` | Contextual data regarding the request and received response |
### `HTTPRetryLog`
This log content is sent to the logger whenever the client attempts to retry HTTP requests.
| Property | Type | Description |
| -------- | ------------------------ | ---------------------------------- |
| type | `LogType['HTTP-Retry']` | The type of log content. Is always set to `HTTP-Retry` |
| content | `{`[`requestParams`](#requestparams)`: [url, init?], lastResponse?: Response, retryAttempt: number, maxRetries: number}` | Contextual data regarding the upcoming retry attempt. <br /><br/>`requestParams`: [parameters](#requestparams) used in the request<br/>`lastResponse`: previous response <br/> `retryAttempt`: the current retry attempt count <br/> `maxRetries`: the maximum number of retries |
### `HTTPResponseGraphQLDeprecationNotice`
This log content is sent to the logger whenever a HTTP response with a `X-Shopify-API-Deprecated-Reason` is received by the client.
| Property | Type | Description |
| -------- | ------------------------ | ---------------------------------- |
| type | `LogType['HTTP-Response-GraphQL-Deprecation-Notice']` | The type of log content. Is always set to `HTTP-Response-GraphQL-Deprecation-Notice` |
| content | `{`[`requestParams`](#requestparams)`: [url, init?], deprecationNotice: string}` | Contextual data regarding the request and received deprecation information |
### `RequestParams`
| Property | Type | Description |
| -------- | ------------------------ | ---------------------------------- |
| url | `string` | Requested URL |
| init? | `{method?: string, headers?: HeaderInit, body?: string}` | The request information |