Docs
Launch GraphOS Studio

Using GraphQL directives in Apollo Client

Configure GraphQL fields and fragments


A decorates part of a or with additional configuration. Tools like can read a GraphQL 's directives and perform custom logic as appropriate.

are preceded by the @ character, like so:

query myQuery($someTest: Boolean) {
experimentalField @skip(if: $someTest)
}

This example shows the @skip directive, which is a built-in directive (i.e., it's part of the GraphQL specification). It demonstrates the following about :

  • Directives can take of their own (if in this case).
  • Directives appear after the declaration of what they decorate (the experimentalField in this case).

@client

The @client directive allows you to resolve client-only data alongside your server data. These are not sent to the .

query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
site
rocket {
type
# resolved locally on the client,
# removed from the request to the server
description @client
}
}
}

For more information about the @client directive, see this section on local-only fields. The @client directive is also useful for client schema mocking before a given field is supported in the API your application is consuming.

@connection

The @connection directive allows you to specify a custom cache key for paginated results. For more information, see this section on the @connection directive.

query Feed($offset: Int, $limit: Int) {
feed(offset: $offset, limit: $limit) @connection(key: "feed") {
...FeedFields
}
}

@defer

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

To use the @defer directive, we apply it to an inline or named that contains all slow-resolving fields:

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

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

For more information about the @defer directive, check out the @defer docs.

@export

If your GraphQL query uses , the local-only fields of that query can provide the values of those variables.

To do so, you apply the @export(as: "variableName") directive, like so:

const GET_CURRENT_AUTHOR_POST_COUNT = gql`
query CurrentAuthorPostCount($authorId: Int!) {
currentAuthorId @client @export(as: "authorId")
postCount(authorId: $authorId)
}
`;

In the query above, the result of the local-only field currentAuthorId is used as the value of the $authorId that's passed to postCount.

You can do this even if postCount is also a local-only field (i.e., if it's also marked as @client).

For more information and other considerations when using the @export directive, check out the local-only fields docs.

@nonreactive
Since 3.8.0

The @nonreactive directive can be used to mark query fields or fragment spreads and is used to indicate that changes to the data contained within the subtrees marked @nonreactive should not trigger rerendering. This allows parent components to fetch data to be rendered by their children without rerendering themselves when the data corresponding with fields marked as @nonreactive change.

Consider an App component that fetches and renders a list of ski trails:

const TrailFragment = gql`
fragment TrailFragment on Trail {
name
status
}
`;
const ALL_TRAILS = gql`
query allTrails {
allTrails {
id
...TrailFragment @nonreactive
}
}
${TrailFragment}
`;
function App() {
const { data, loading } = useQuery(ALL_TRAILS);
return (
<main>
<h2>Ski Trails</h2>
<ul>
{data?.trails.map((trail) => (
<Trail key={trail.id} id={trail.id} />
))}
</ul>
</main>
);
}

The Trail component renders a trail's name and status and allows the user to execute a to toggle the status of the trail between "OPEN" and "CLOSED":

const Trail = ({ id }) => {
const [updateTrail] = useMutation(UPDATE_TRAIL);
const { data } = useFragment({
fragment: TrailFragment,
from: {
__typename: "Trail",
id,
},
});
return (
<li key={id}>
{data.name} - {data.status}
<input
checked={data.status === "OPEN" ? true : false}
type="checkbox"
onChange={(e) => {
updateTrail({
variables: {
trailId: id,
status: e.target.checked ? "OPEN" : "CLOSED",
},
});
}}
/>
</li>
);
};

Notice that the Trail component isn't receiving the entire trail object via props, only the id which is used along with the fragment document to create a live binding for each trail item in the cache. This allows each Trail component to react to the cache updates for a single trail independently. Updates to a trail's status will not cause the parent App component to rerender since the @nonreactive directive is applied to the TrailFragment spread, a fragment that includes the status field.

Previous
Fragments
Next
Error handling
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company