Skip to main content

Using the @defer directive

The @defer directive allows clients to prioritize part of a GraphQL query without having to make more requests to fetch the remaining information. You can use this directive to minimize the overhead when fetching data from Shopify. Rather than maintaining multiple queries and performing several API calls, clients can make a single request and receive the data as a stream of responses.


  • Your app can make requests to the GraphQL Storefront API.

Anchor to Choosing a supported clientChoosing a supported client

The @defer directive is currently a draft contribution to the GraphQL specification. To maximize compatibility with clients that support some variant of the @defer specification today, we have implemented the spec as of its description on August 23, 2022.

You can use one of the following compatible GraphQL libraries:

Note

The @defer directive is not yet supported on Hydrogen.


The @defer directive can be applied to fragment spreads and inline fragments in a GraphQL query. Fields cannot use @defer directly.

Many client libraries support @defer in two ways: after each response is received, or once all responses have been received. To get the benefits of @defer it's strongly suggested to handle each response as it arrives, so the client can begin rendering as soon as possible.

The following example retrieves details about a product before the list of related products. It uses @defer to deprioritize the productRecommendations query, but still receive those recommendations afterward without making an additional request. Each response (including the undeferred part) begins with --graphql.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

query product($productId: ID!) {
product(id: $productId) {
id
title
}
... relatedProducts @defer
}

fragment relatedProducts on QueryRoot {
productRecommendations(productId: $productId) {
id
title
}
}

GraphQL Response

Headers

content-type: multipart/mixed; boundary=graphql

Body

--graphql
Content-Type: application/json
Content-Length: 108

{
"data":{
"product": {
"id": "gid://shopify/Product/191637365",
"title": "Black Dot Dress Shirt"
}
},
"hasNext": true
}

--graphql
Content-Type: application/json
Content-Length: 286

{
"incremental": [
{
"path": [],
"data": {
"productRecommendations": [
{ "id": "gid://shopify/Product/1328794861624", "title": "Baseball t-shirt" },
{ "id": "gid://shopify/Product/1328794894392", "title": "Fancy t-shirt" },
{ "id": "gid://shopify/Product/1328795091000", "title": "Sports t-shirt" }
]
}
}
],
"hasNext": false
}

--graphql--

In the provided example, the @defer directive is used with the relatedProducts fragment. This fragment, defined separately, includes the productRecommendations query. By using @defer on this fragment, we're instructing to the server that it's acceptable to return the data for productRecommendations after the product.

When the server processes this query, it first returns the main product data, indicated by the "hasNext": true in the response. The server then continues to process the deferred fragment and returns the product recommendations. You will know that all parts have been received when the response contains "hasNext": false.

There might be some situations where Shopify believes it to be more efficient to serve the deferred fragment inline, rather than as a second streamed value. In this case, the initial response will contain all of the requested JSON. As before, streamed responses are complete when indicated by the presence of "hasNext": false in the response.


Anchor to Fetching carrier-calculated rates for the cart using ,[object Object]Fetching carrier-calculated rates for the cart using @defer

As of 2024-07, Cart#deliveryGroups supports a new withCarrierRates argument, used to indicate intent to fetch carrier-calculated rates. This capability requires mandatory use of @defer directive to ensure that the calculation of the delivery rates does not delay the cart response.

When fetching the delivery groups using withCarrierRates: true inside a @defer fragment, the cart will initially calculate (excluding the delivery rates) and resolve the response, and then stream the fragment containing the rates once they're available.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

query cart($cartId: ID!) {
cart(id: $cartId) {
cost {
subtotalAmount {
amount
currencyCode
}
totalAmount {
amount
currencyCode
}
}
... DeliveryGroups @defer
}
}

fragment DeliveryGroups on Cart {
deliveryGroups(first: 10, withCarrierRates: true) {
edges {
node {
deliveryOptions {
title
handle
estimatedCost {
amount
currency
}
}
selectedDeliveryOption {
title
handle
estimatedCost {
amount
currencyCode
}
}
}
}
}
}

GraphQL Response

Headers

content-type: multipart/mixed; boundary=graphql

Body

--graphql
Content-Type: application/json
Content-Length: 156

{
"data": {
"cart": {
"cost": {
"subtotalAmount": {
"amount": "155.99",
"currencyCode": "CAD"
},
"totalAmount": {
"amount": "155.99",
"currencyCode": "CAD"
}
}
}
},
"hasNext": true
}

--graphql
Content-Type: application/json
Content-Length: 271

{
"incremental": [
{
"path": [
"cart"
],
"data": {
"deliveryGroups": {
"edges": [
{
"node": {
"deliveryOptions": [
{
"title": "Priority",
"handle": "b709d6e6b08ffb9d93d8bc0a23d76d44",
"estimatedCost": {
"amount": "21.86",
"currencyCode": "CAD"
}
}
],
"selectedDeliveryOption": null
}
}
]
}
}
}
],
"hasNext": false
}

--graphql--

In the provided example, the first response fragment contains the non-deferred cart data, which is returned without delay from the pending carrier-calculated rates. Once the rates become available, the server returns the available delivery groups of the cart in the second fragment using the @defer protocol.

Note

Fetching the carrier-calculated rates will not automatically select a delivery option. To select a carrier-calculated rate, please refer to cartSelectedDeliveryOptionsUpdate. Furthermore, the cost field is not updated when fetching of carrier-calculated rates.


Streamed responses for @defer follow the Incremental Delivery over HTTP specification for GraphQL. In particular, that means clients receive a multipart content response, indicated by the HTTP header of content-type: multipart/mixed; boundary=graphql. If the client only supports a maximum of HTTP/1.1, the response will also be served using transfer-encoding: chunked, which is no longer necessary as of HTTP/2.


Was this page helpful?