1 | # GraphQL Client
|
2 |
|
3 | 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.
|
4 |
|
5 |
|
6 | ## Getting Started
|
7 | Using your preferred package manager, install this package in a project:
|
8 |
|
9 | ```sh
|
10 | yarn add @shopify/graphql-client
|
11 | ```
|
12 |
|
13 | ```sh
|
14 | npm install @shopify/graphql-client --s
|
15 | ```
|
16 |
|
17 | ```sh
|
18 | pnpm add @shopify/graphql-client
|
19 | ```
|
20 |
|
21 | ### CDN
|
22 | The UMD builds of each release version are available via the [`unpkg` CDN](https://unpkg.com/browse/@shopify/graphql-client@latest/dist/umd/)
|
23 |
|
24 | ```html
|
25 | // The minified `v0.9.3` version of the GraphQL API Client
|
26 | <script src="https://unpkg.com/@shopify/graphql-client@0.9.3/dist/umd/graphql-client.min.js"></script>
|
27 |
|
28 | <script>
|
29 | const client = ShopifyGraphQLClient.createGraphQLClient({...});
|
30 | </script>
|
31 | ```
|
32 |
|
33 | ## Client initialization
|
34 |
|
35 | ```typescript
|
36 | import {createGraphQLClient} from '@shopify/graphql-client';
|
37 |
|
38 | const client = createGraphQLClient({
|
39 | url: 'http://your-shop-name.myshopify.com/api/2023-10/graphql.json',
|
40 | headers: {
|
41 | 'Content-Type': 'application/json',
|
42 | 'X-Shopify-Storefront-Access-Token': 'public-token',
|
43 | },
|
44 | retries: 1
|
45 | });
|
46 | ```
|
47 |
|
48 | ### Create a server enabled client using a custom Fetch API
|
49 |
|
50 | 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.
|
51 |
|
52 | ```typescript
|
53 | import {createGraphQLClient} from '@shopify/graphql-client';
|
54 | import {fetch as nodeFetch} from 'node-fetch';
|
55 |
|
56 | const client = createGraphQLClient({
|
57 | url: 'http://your-shop-name.myshopify.com/api/2023-10/graphql.json',
|
58 | headers: {
|
59 | 'Content-Type': 'application/json',
|
60 | 'X-Shopify-Storefront-Access-Token': 'public-token',
|
61 | },
|
62 | customFetchApi: nodeFetch
|
63 | });
|
64 | ```
|
65 |
|
66 | ### `createGraphQLClient()` parameters
|
67 |
|
68 | | Property | Type | Description |
|
69 | | -------- | ------------------------ | ---------------------------------- |
|
70 | | url | `string` | The GraphQL API URL |
|
71 | | headers | `Record<string, string \| string[]>` | Headers to be included in requests |
|
72 | | 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`. |
|
73 | | 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()`. |
|
74 | | logger? | `(logContent: `[`HTTPResponseLog`](#httpresponselog)`\|`[`HTTPRetryLog`](#httpretrylog)`) => void` | A logger function that accepts [log content objects](#log-content-types). This logger will be called in certain conditions with contextual information. |
|
75 |
|
76 | ## Client properties
|
77 |
|
78 | | Property | Type | Description |
|
79 | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
80 | | config | `{url: string, headers: Record<string, string \| string[]>, retries: number}` | Configuration for the client |
|
81 | | 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 |
|
82 | | 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) |
|
83 | | 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. |
|
84 |
|
85 | ## `RequestOptions` properties
|
86 |
|
87 | | Name | Type | Description |
|
88 | | ---------- | --------------------- | ---------------------------------------------------------------- |
|
89 | | variables? | `Record<string, any>` | Variable values needed in the graphQL operation |
|
90 | | url? | `string` | Alternative request API URL |
|
91 | | headers? | `Record<string, string \| string[]>` | Additional and/or replacement headers to be used in the request |
|
92 | | 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`.|
|
93 |
|
94 | ## `ClientResponse<TData>`
|
95 |
|
96 | | Name | Type | Description |
|
97 | | ----------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
98 | | 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`. |
|
99 | | 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`. |
|
100 | | 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. |
|
101 |
|
102 | ## `ClientStreamResponse<TData>`
|
103 |
|
104 | | Name | Type | Description |
|
105 | | ----------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
106 | | 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`. |
|
107 | | 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`. |
|
108 | | 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. |
|
109 | | hasNext | `boolean` | Flag to indicate whether the response stream has more incoming data |
|
110 |
|
111 | ## `ResponseErrors`
|
112 |
|
113 | | Name | Type | Description |
|
114 | | ----------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
115 | | networkStatusCode? | `number` | HTTP response status code |
|
116 | | message? | `string` | The provided error message |
|
117 | | graphQLErrors? | `any[]` | The GraphQL API errors returned by the server |
|
118 | | response? | `Response` | The raw response object from the network fetch call |
|
119 |
|
120 | ## Usage examples
|
121 |
|
122 | ### Query for a product
|
123 |
|
124 | ```typescript
|
125 | const productQuery = `
|
126 | query ProductQuery($handle: String) {
|
127 | product(handle: $handle) {
|
128 | id
|
129 | title
|
130 | handle
|
131 | }
|
132 | }
|
133 | `;
|
134 |
|
135 | const {data, errors, extensions} = await client.request(productQuery, {
|
136 | variables: {
|
137 | handle: 'sample-product',
|
138 | },
|
139 | });
|
140 | ```
|
141 |
|
142 | ### Query for product info using the `@defer` directive
|
143 |
|
144 | ```typescript
|
145 | const productQuery = `
|
146 | query ProductQuery($handle: String) {
|
147 | product(handle: $handle) {
|
148 | id
|
149 | handle
|
150 | ... @defer(label: "deferredFields") {
|
151 | title
|
152 | description
|
153 | }
|
154 | }
|
155 | }
|
156 | `;
|
157 |
|
158 | const responseStream = await client.requestStream(productQuery, {
|
159 | variables: {handle: 'sample-product'},
|
160 | });
|
161 |
|
162 | // await available data from the async iterator
|
163 | for await (const response of responseStream) {
|
164 | const {data, errors, extensions, hasNext} = response;
|
165 | }
|
166 | ```
|
167 |
|
168 | ### Add additional custom headers to the API request
|
169 |
|
170 | ```typescript
|
171 | const productQuery = `
|
172 | query ProductQuery($handle: String) {
|
173 | product(handle: $handle) {
|
174 | id
|
175 | title
|
176 | handle
|
177 | }
|
178 | }
|
179 | `;
|
180 |
|
181 | const {data, errors, extensions} = await client.request(productQuery, {
|
182 | variables: {
|
183 | handle: 'sample-product',
|
184 | },
|
185 | headers: {
|
186 | 'Shopify-Storefront-Id': 'shop-id',
|
187 | },
|
188 | });
|
189 | ```
|
190 |
|
191 | ### Use an updated API URL in the API request
|
192 |
|
193 | ```typescript
|
194 | const productQuery = `
|
195 | query ProductQuery($handle: String) {
|
196 | product(handle: $handle) {
|
197 | id
|
198 | title
|
199 | handle
|
200 | }
|
201 | }
|
202 | `;
|
203 |
|
204 | const {data, errors, extensions} = await client.request(productQuery, {
|
205 | variables: {
|
206 | handle: 'sample-product',
|
207 | },
|
208 | url: 'http://your-shop-name.myshopify.com/api/unstable/graphql.json',
|
209 | });
|
210 | ```
|
211 |
|
212 | ### Set a custom retries value in the API request
|
213 |
|
214 | ```typescript
|
215 | const shopQuery = `
|
216 | query ShopQuery {
|
217 | shop {
|
218 | name
|
219 | id
|
220 | }
|
221 | }
|
222 | `;
|
223 |
|
224 | // 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
|
225 | const {data, errors, extensions} = await client.request(shopQuery, {
|
226 | retries: 2,
|
227 | });
|
228 | ```
|
229 |
|
230 | ### Provide GQL query type to `client.request()` and `client.requestStream()`
|
231 |
|
232 | ```typescript
|
233 | import {print} from 'graphql/language';
|
234 |
|
235 | // GQL operation types are usually auto generated during the application build
|
236 | import {CollectionQuery, CollectionDeferredQuery} from 'types/appTypes';
|
237 | import collectionQuery from './collectionQuery.graphql';
|
238 | import collectionDeferredQuery from './collectionDeferredQuery.graphql';
|
239 |
|
240 | const {data, errors, extensions} = await client.request<CollectionQuery>(
|
241 | print(collectionQuery),
|
242 | {
|
243 | variables: {
|
244 | handle: 'sample-collection',
|
245 | },
|
246 | }
|
247 | );
|
248 |
|
249 | const responseStream = await client.requestStream<CollectionDeferredQuery>(
|
250 | print(collectionDeferredQuery),
|
251 | {
|
252 | variables: {handle: 'sample-collection'},
|
253 | }
|
254 | );
|
255 | ```
|
256 |
|
257 | ### Using `client.fetch()` to get API data
|
258 |
|
259 | ```typescript
|
260 | const shopQuery = `
|
261 | query shop {
|
262 | shop {
|
263 | name
|
264 | id
|
265 | }
|
266 | }
|
267 | `;
|
268 |
|
269 | const response = await client.fetch(shopQuery);
|
270 |
|
271 | if (response.ok) {
|
272 | const {errors, data, extensions} = await response.json();
|
273 | }
|
274 | ```
|
275 |
|
276 | ## Log Content Types
|
277 |
|
278 | ### `HTTPResponseLog`
|
279 |
|
280 | This log content is sent to the logger whenever a HTTP response is received by the client.
|
281 |
|
282 | | Property | Type | Description |
|
283 | | -------- | ------------------------ | ---------------------------------- |
|
284 | | type | `LogType['HTTP-Response']` | The type of log content. Is always set to `HTTP-Response` |
|
285 | | content | `{`[`requestParams`](#requestparams)`: [url, init?], response: Response}` | Contextual data regarding the request and received response |
|
286 |
|
287 | ### `HTTPRetryLog`
|
288 |
|
289 | This log content is sent to the logger whenever the client attempts to retry HTTP requests.
|
290 |
|
291 | | Property | Type | Description |
|
292 | | -------- | ------------------------ | ---------------------------------- |
|
293 | | type | `LogType['HTTP-Retry']` | The type of log content. Is always set to `HTTP-Retry` |
|
294 | | 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 |
|
295 |
|
296 | ### `RequestParams`
|
297 |
|
298 | | Property | Type | Description |
|
299 | | -------- | ------------------------ | ---------------------------------- |
|
300 | | url | `string` | Requested URL |
|
301 | | init? | `{method?: string, headers?: HeaderInit, body?: string}` | The request information |
|