Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL Federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

Using the @defer directive in Apollo iOS


⚠️ The @defer directive is currently an experimental feature in Apollo iOS and is available for use from version 1.14.0. If you have feedback on the feature, please let us know via a new GitHub issue or search through any existing @defer-related issues.

The @defer directive enables your queries to receive data for specific asynchronously. This is helpful whenever some fields in a take much longer to resolve than the others.

For 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. If we include all of those fields in a single query we want to be able to display the basic profile information as soon as it's available instead of waiting for the friends to resolve.

To achieve this, we can apply the @defer 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)
... on User @defer(label: "deferredFriends") {
friends {
id
}
}
}
}

In the generated code for this query the asUser type case will be optional, it will also have a fragments container in which there will be an optional fragment with the same name as the label property value used in the query. The deferred fragment is optional because it will not be returned with the basic fields, it will be returned in a separate response instead.

All deferred are annotated with the @Deferred property wrapper. This allows you to check the state of any deferred fragment through it's projected value. When the fields of the deferred fragment are received, another response will be returned with all basic fields and friends fields too.

client.fetch(query: ExampleQuery()) { result in
switch (result) {
case let .success(data):
if case .fulfilled = data.data?.person.asUser?.fragments.$deferredFriends {
print("Query Success! Received all fields.")
} else {
print("Query Success! Received basic fields only.")
}
case let .failure(error):
print("Query Failure! \(error)")
}
}

Will print something like this:

Query Success! Received basic fields only.
Query Success! Received all fields.

Requirements

  • The @defer directive must be used on a type case.
  • The @defer directive must have a label property.

Caching

Reading cache data

Typical caching behaviour and functionality is supported for with the @defer directive too. Cache reads behave slightly differently to network requests in that all cached data, including any cached deferred fragments, will be returned in the first response. In other words, if the entire query has been cached then there will only be one response with all basic and deferred fragments. If you manually added only the basic data to the cache then you would receive only the basic data in the first response and depending on the cache policy used may get the deferred fragments returned from the network separately.

Writing cache data

We're still building Selection Set Initializer compatibility for deferred fragments. Until that is complete, writing to the cache requires that the local cache , or named fragment , be initialized using the private .init(_dataDict: DataDict) function.

Previous
Persisted Queries
Next
Introduction
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc., d/b/a Apollo GraphQL.

Privacy Policy

Company