Docs
Launch GraphOS Studio

Using the @defer directive in Apollo Client

Receive query response data incrementally


The @defer directive is currently at the General Availability stage in Apollo Client, and is available by installing @apollo/client@latest. If you have feedback on it, please let us know via GitHub issues.

Beginning with version 3.7.0, provides preview support for the @defer directive. This enables your queries to receive data for specific incrementally, instead of receiving all data at the same time. This is helpful whenever some fields in a take much longer to resolve than others.

For a to defer successfully, the queried endpoint must also support the @defer . -based @defer support is also at the General Availability stage in Apollo Router and is compatible with all federation-compatible subgraph libraries.

Example

Let's say we're building a social media application that can quickly fetch a user's basic profile information, but retrieving that user's friends takes longer.

allows us to declare all the our UI requires in a single , but this also means that our query will be as slow as the field that takes the longest to resolve. The @defer allows us to mark parts of the that are not necessary for our app's initial render which will be resolved once it becomes available.

To achieve this, we apply the @defer to an in-line that contains all slow-resolving related to friend data:

query PersonQuery($personId: ID!) {
person(id: $personId) {
# Basic fields (fast)
id
firstName
lastName
# Friend fields (slower)
... @defer {
friends {
id
}
}
}
}

Using this syntax, if the queried server supports @defer, our client can receive the "Basic " in an initial response payload, followed by a supplementary payload containing the "Friend fields".

Let's look at an example in React. Here's we can assume GET_PERSON is the above , PersonQuery, with a deferred list of friends' ids:

app.jsx
import { gql, useQuery } from "@apollo/client";
function App() {
const { loading, error, data } = useQuery(GET_PERSON, {
variables: {
id: 1,
},
});
if (loading) return "Loading...";
if (error) return `Error! ${error.message}`;
return (
<>
Welcome, {data.firstName} {data.lastName}!
<details>
<summary>Friends list</summary>
{data.friends ? (
<ul>
{data.friends.map((id) => (
<li>{id}</li>
))}
</ul>
) : null}
</details>
</>
);
}

When our call to the useQuery hook first resolves with an initial payload of data, loading will go from true to false and firstName and lastName will be populated with the values from the server. Our deferred fields will not exist as keys on data yet, so we must add conditional logic that checks for their presence. When subsequent chunks of deferred data arrive, useQuery will re-render (loading remains false between re-renders from deferred multipart responses) and data will include the deferred data as they arrive.

For this reason, @defer can be thought of as a tool to improve initial rendering speeds when some slower data will be displayed below the fold or offscreen. In this case, we're rendering the friends list inside a <details> element which is closed by default, avoiding any layout shift as the friends data arrives.

Using with code generation

If you currently use GraphQL Code Generator for your codegen needs, you'll need to use the Codegen version 4.0.0 or higher. Please refer to the Codegen documentation for more information.

Usage in React Native

In order to use @defer in a React Native application, additional configuration is required. See the React Native docs for more information.

Next
Introduction
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company