Launch GraphOS Studio

Using GraphQL directives in Apollo Client

Configure GraphQL fields and fragments

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

Directives 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:

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


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

query LaunchDetails($launchId: ID!) {
launch(id: $launchId) {
rocket {
# 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 GraphQL API your application is consuming.


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") {


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 query take much longer to resolve than others.

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

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

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


If your GraphQL query uses variables, 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:

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 variable 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.


⚠️ The @nonreactive directive is currently at the preview stage in Apollo Client, and is available by installing @apollo/client@alpha. If you have feedback on it, please let us know via GitHub issues.

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 {
const ALL_TRAILS = gql`
query allTrails {
allTrails {
...TrailFragment @nonreactive
function App() {
const { data, loading } = useQuery(ALL_TRAILS);
return (
<h2>Ski Trails</h2>
{data? => (
<Trail key={} id={} />

The Trail component renders a trail's name and status and allows the user to execute a mutation 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",
return (
<li key={id}>
{} - {data.status}
checked={data.status === "OPEN" ? true : false}
onChange={(e) => {
variables: {
trailId: id,
status: ? "OPEN" : "CLOSED",

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.

Error handling
Edit on GitHubEditForumsDiscord