Docs
Launch GraphOS Studio

Using the @defer directive in Apollo Client

Receive query response data incrementally


The @defer directive is currently at the

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

. 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 query to defer fields successfully, the queried endpoint must also support the @defer directive. -based @defer support is also at the General Availability stage in

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 fields our UI requires in a single query, but this also means that our query will be as slow as the field that takes the longest to resolve. The @defer directive allows us to mark parts of the query 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 directive to an in-line that contains all slow-resolving fields 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 fields" 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 query, 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

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

for more information.

Next
Introduction
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company