Your Apollo Client queries can include local-only fields that aren't defined in your GraphQL server's schema. The values for these fields are calculated locally using any logic you want, such as reading data from
A single query can include both local-only fields and fields that are fetched from your GraphQL server.
Let's say we're building an e-commerce application. Most of a product's details are stored on our back-end server, but we want to define an
isInCart boolean field that's local to the client. First, we create a field policy for
A field policy specifies custom logic for how a single GraphQL field is fetched from and written to your Apollo Client cache. You can define field policies for both local-only fields and remotely fetched fields.
Field policies are primarily documented in Customizing the behavior of cached fields. This article specifically describes how to use them with local-only fields.
You define your application's field policies in a map that you provide to the constructor of Apollo Client's
InMemoryCache. Each field policy is a child of a particular type policy (much like the corresponding field is a child of a particular type).
Here's a sample
InMemoryCache constructor that defines a field policy for
The field policy above defines a
read function for the
isInCart field. Whenever you query a field that has a
read function, the cache calls that function to calculate the field's value. In this case, the
read function returns whether the queried product's ID is in the
CART array in
You can use
read functions to perform any sort of logic you want, including:
- Manually executing other cache operations
- Calling helper utilities or libraries to prepare, validate, or sanitize data
- Fetching data from a separate store
- Logging usage metrics
If you query a local-only field that doesn't define a
readfunction, Apollo Client performs a default cache lookup for the field. See Storing local state in the cache for details.
Now that we've defined a field policy for
isInCart, we can include the field in a query that also queries our back-end server, like so:
That's it! The
@client directive tells Apollo Client that
isInCart is a local-only field. Because
isInCart is local-only, Apollo Client omits it from the query it sends to our server to fetch
price. The final query result is returned only after all local and remote fields are populated.
You can use Apollo Client to query local state, regardless of how you store that state. Apollo Client provides a couple of optional but helpful mechanisms for representing local state:
Apollo Client reactive variables are great for representing local state:
- You can read and modify reactive variables from anywhere in your application, without needing to use a GraphQL operation to do so.
- Unlike the Apollo Client cache, reactive variables don't enforce data normalization, meaning you can store data in any format you want.
- If a field's value depends on the value of a reactive variable, and that variable's value changes, every active query that includes the field automatically refreshes.
Returning to our e-commerce application, let's say we want to fetch a list of the item IDs in a user's cart, and this list is stored locally. The query to do that looks like this:
Let's initialize a reactive variable to store our local list of cart items, like so:
This initializes a reactive variable that contains an empty array. We can get this variable's current value by calling
cartItemsVar(), and we can set a new value by calling
Next, let's define the field policy for
cartItems. As always, we pass this to the constructor of
read function returns the value of our reactive variable whenever
cartItems is queried.
Now, let's create a button component that enables the user to add a product to their cart:
submit call, the value of
cartItemsVar is updated by appending
productId. When this happens, Apollo Client notifies every active query that includes the
Cart component that uses the
GET_CART_ITEMS query and therefore refreshes automatically whenever the value of
Storing local state directly in the Apollo Client cache provides some advantages, but usually requires more code than using reactive variables:
- You don't have to define a field policy for local-only fields that are present in the cache. If you query a field that doesn't define a
readfunction, by default Apollo Client attempts to fetch that field's value directly from the cache.
- When you modify a cached field with
writeFragment, every active query that includes the field automatically refreshes.
Let's say our application defines the following query:
isLoggedIn field of this query is a local-only field. We can use the
writeQuery method to write a value for this field directly to the Apollo Client cache, like so:
This writes a boolean value according to whether
localStorage includes a
token item, indicating an active session.
Now our application's components can render according to the value of the
isLoggedIn field, without our needing to define a
read function for it:
Note that even if you do store local data as fields in the Apollo Client cache, you can (and probably should!) still define
read functions for those fields. A
read function can execute helpful custom logic, such as returning a default value if a field isn't present in the cache.
The way you modify the value of a local-only field depends on how you store that field:
If you're using a reactive variable, all you need to do is set the reactive variable's new value. Apollo Client automatically detects this change and triggers a refresh of every active operation that includes an affected field.
If you're using the cache directly, call one of
cache.modify(all documented here) to modify cached fields. Like reactive variables, all of these methods trigger a refresh of every affected active operation.
If you're using another storage method, such as
localStorage, set the field's new value in whatever method you're using. Then, you can force a refresh of every affected operation by calling
cache.evict. In your call, provide both the
idof your field's containing object and the name of the local-only field.
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:
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
You can do this even if
postCount is also a local-only field (i.e., if it's also marked as
Considerations for using
To use the
@exportdirective, a field must also use the
@clientdirective. In other words, only local-only fields can be used as variable values.
A field that
@exports a variable value must appear before any fields that use that variable.
If multiple fields in an operation use the
@exportdirective to assign their value to the same variable, the field listed last takes precedence. When this happens in development mode, Apollo Client logs a warning message.
At first glance, the
@exportdirective appears to violate the GraphQL specification's requirement that the execution order of an operation must not affect its result:
…the resolution of fields other than top‐level mutation fields must always be side effect‐free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.
@exported variable values are populated before an operation is sent to a remote server. Only local-only fields can use the
@exportdirective, and those fields are stripped from operations before they're transmitted.