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.
## Requirements
- Your app can make [requests](/docs/api/storefront) to the GraphQL Storefront API.
## Choosing a supported client
The `@defer` directive is currently a [draft contribution](https://github.com/graphql/graphql-spec/blob/feac5a54c6a95c1d4f7804bfaeb268c8bd206f2c/CONTRIBUTING.md#stage-2-draft) 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](https://github.com/graphql/graphql-spec/tree/01d7b98f04810c9a9db4c0e53d3c4d54dbf10b82).
You can use one of the following compatible GraphQL libraries:
- Shopify GraphQL Client
- Shopify Storefront API Client
- Apollo Client
- urql
> Note:
> The `@defer` directive is not yet supported on [Hydrogen](/docs/storefronts/headless/hydrogen).
## Optimize a query using `@defer`
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`.
```graphql
query product($productId: ID!) {
product(id: $productId) {
id
title
}
... relatedProducts @defer
}
fragment relatedProducts on QueryRoot {
productRecommendations(productId: $productId) {
id
title
}
}
```
```
content-type: multipart/mixed; boundary=graphql
```
```json
--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.
## 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.
```graphql
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
}
}
}
}
}
}
```
```
content-type: multipart/mixed; boundary=graphql
```
```json
--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`](/docs/api/storefront/latest/mutations/cartSelectedDeliveryOptionsUpdate). Furthermore, the `cost` field is not updated when fetching of carrier-calculated rates.
## Technical details
Streamed responses for `@defer` follow the [Incremental Delivery over HTTP](https://github.com/graphql/graphql-over-http/blob/c1acb54053679f08939c9cdcee00b72d77c42211/rfcs/IncrementalDelivery.md) 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`.