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 , which is a built-in directive (i.e., it's part of the GraphQL specification). It demonstrates the following about :

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

@client

The @client 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 , see this section on local-only fields. The @client is also useful for client schema mocking before a given is supported in the API your application is consuming.

@connection

The @connection 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, 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.

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

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 , check out the @defer docs.

@export

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

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

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

In the above, the result of the local-only 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 (i.e., if it's also marked as @client).

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

@nonreactive
Since 3.8.0

The @nonreactive can be used to mark or 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 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 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 is applied to the TrailFragment spread, a that includes the status .

Previous
Fragments
Next
Error handling
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company