Skip to content

Commit

Permalink
Edits to cache interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Barlow authored and Stephen Barlow committed Mar 22, 2021
1 parent 63cca5f commit 37c0135
Showing 1 changed file with 60 additions and 48 deletions.
108 changes: 60 additions & 48 deletions docs/source/caching/cache-interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,70 @@ title: Reading and writing data to the cache
sidebar_title: Reading and writing
---

You can read and write data directly to the Apollo Client cache, _without_ communicating with your GraphQL server. You can use methods that conform to the shape and behavior of standard [GraphQL queries](#using-schema-supported-operations) and [GraphQL fragments](#accessing-arbitrary-objects), or you can [directly modify cached fields](#using-cachemodify).
You can read and write data directly to the Apollo Client cache, _without_ communicating with your GraphQL server. You can interact with data that you previously fetched from your server, _and_ with data that's only available [locally](../local-state/local-state-management/).

> All code samples below assume that you have initialized an instance of `ApolloClient` and that you have imported the `gql` function from `@apollo/client`.
Apollo Client supports multiple strategies for interacting with cached data:

| Strategy | API | Description |
|----------|-----|-------------|
| [Using GraphQL queries](#using-graphql-queries) | `readQuery` / `writeQuery` | Enables you to use standard GraphQL queries for managing both remote and local data. |
| [Using GraphQL fragments](#using-graphql-fragments) | `readFragment` / `writeFragment` | Enables you to access the fields of any cached object without composing an entire query to reach that object. |
| [Directly modifying cached fields](#using-cachemodify) | `cache.modify` | Enables you to manipulate cached data without using GraphQL at all. |

You can use whichever combination of strategies and methods are most helpful for your use case.

> All code samples below assume that you have initialized an instance of `ApolloClient` and that you have imported the `gql` function from `@apollo/client`. If you haven't, [get started](../get-started).
>
>In a React component, you can access your instance of `ApolloClient` using [`ApolloProvider`](https://www.apollographql.com/docs/react/api/react/hooks/#the-apolloprovider-component) and the [`useApolloClient`](https://www.apollographql.com/docs/react/api/react/hooks/#useapolloclient) hook.
## Using GraphQL queries

You can use the `readQuery` and `writeQuery` methods of `ApolloClient` to read and write cache data. These methods use GraphQL queries that are similar (or identical) to queries that you execute on your server.
You can read and write cache data using GraphQL queries that are similar (or even identical) to queries that you execute on your server:

### `readQuery`

The `readQuery` method enables you to execute a GraphQL query directly on your cache, like so:

```js
```js{12-17}
const READ_TODO = gql`
query ReadTodo($id: ID!) {
todo(id: $id) {
id
text
completed
}
}
`;
// Fetch the cached to-do item with ID 5
const { todo } = client.readQuery({
query: gql`
query ReadTodo {
todo(id: 5) {
id
text
completed
}
}
`,
query: READ_TODO,
variables: { // Provide any required variables here
id: 5,
},
});
```

* If your cache contains data for _all_ of the query's fields, `readQuery` returns a data object in the shape of the query, just like your GraphQL server does.

* If the cache is missing data for _any_ of the query's fields, `readQuery` returns `null`. It does _not_ attempt to fetch data from your GraphQL server.
If your cache contains data for _all_ of the query's fields, `readQuery` returns an object that matches the shape of the query:

> Prior to Apollo Client 3.3, `readQuery` threw a `MissingFieldError` exception to report missing fields. Beginning with Apollo Client 3.3, `readQuery` always returns `null` to indicate that fields are missing.
```js
{
todo: {
__typename: 'Todo', // __typename is automatically included
id: 5,
text: 'Buy oranges 🍊',
completed: true
}
}
```

**Do not modify the return value of `readQuery`.** The same object might be returned to multiple components. To update data in the cache, instead create a replacement object and pass it to [`writeQuery`](#writequery-and-writefragment).
**Do not modify this object directly.** The same object might be returned to multiple components. To update data in the cache, instead create a replacement object and pass it to [`writeQuery`](#writequery-and-writefragment).

#### Providing variables
If the cache is missing data for _any_ of the query's fields, `readQuery` returns `null`. It does _not_ attempt to fetch data from your GraphQL server.

You can provide GraphQL variables to `readQuery` like so:
The query you provide `readQuery` can include fields that are _not_ defined in your GraphQL server's schema (i.e., [local-only fields](../local-state/managing-state-with-field-policies/)).

```js{11-13}
// Fetch the cached to-do item with ID 5
const { todo } = client.readQuery({
query: gql`
query ReadTodo($id: Int!) {
todo(id: $id) {
id
text
completed
}
}`,
variables: {
id: 5,
},
});
```
> Prior to Apollo Client 3.3, `readQuery` threw a `MissingFieldError` exception to report missing fields. Beginning with Apollo Client 3.3, `readQuery` always returns `null` to indicate that fields are missing.
### `writeQuery`

Expand Down Expand Up @@ -91,11 +98,16 @@ client.writeQuery({

This example creates (or edits) a cached `Todo` object with ID `5`.

Note that any changes you make to cached data with `writeQuery` are **not** pushed to your GraphQL server. If you reload your environment, these changes disappear.
Note the following about `writeQuery`:

* Any changes you make to cached data with `writeQuery` are **not** pushed to your GraphQL server. If you reload your environment, these changes disappear.
* The shape of your query is _not_ enforced by your GraphQL server's schema:
* The query can include fields that are _not_ present in your schema.
* You can (but usually shouldn't) provide values for schema fields that are _invalid_ according to your schema.

#### Editing existing data

By default, if your cache _already_ contains a `Todo` object with ID `5`, `writeQuery` overwrites the fields that are included in `data` (other fields are preserved):
In the example above, if your cache _already_ contains a `Todo` object with ID `5`, `writeQuery` overwrites the fields that are included in `data` (other fields are preserved):

```js{6-7,17-18}
// BEFORE
Expand All @@ -119,19 +131,17 @@ By default, if your cache _already_ contains a `Todo` object with ID `5`, `write
dueDate: '2022-07-02'
}
}
```

> If you include a field in `query` but don't include a _value_ for it in `data`, the field's current cached value is preserved.

## Using GraphQL fragments

### `readFragment`
You can read and write cache data using GraphQL fragments on _any_ normalized cache object. This provides more "random access" to your cached data than `readQuery`/`writeQuery`, which require a complete valid query.

The `readFragment` method enables you to read data from _any_ normalized cache object that was stored as part of _any_ query result. Unlike with `readQuery`, calls to `readFragment` do not need to conform to the structure of one of your data graph's supported queries.
### `readFragment`

Here's an example that fetches a particular item from a to-do list:
This example fetches the same data as [the example for `readQuery`](#readquery) using `readFragment` instead:

```js
const todo = client.readFragment({
Expand All @@ -146,11 +156,11 @@ const todo = client.readFragment({
});
```

The first argument, `id`, is the value of the unique identifier for the object you want to read from the cache. By default, this is the value of the object's `id` field, but you can [customize this behavior](./cache-configuration/#generating-unique-identifiers).
Unlike `readQuery`, `readFragment` requires an `id` option. This option specifies the unique identifier for the object in your cache. [By default](cache-configuration/#default-identifier-generation), this identifier has the format `<_typename>:<id>` (which is why we provide `Todo:5` above). You can [customize this identifier](./cache-configuration/#customizing-identifier-generation-by-type).

In the example above, `readFragment` returns `null` if no `Todo` object with an `id` of `5` exists in the cache, or if the object exists but is missing the `text` or `completed` fields.
In the example above, `readFragment` returns `null` if no `Todo` object with ID `5` exists in the cache, or if the object exists but is missing a value for either `text` or `completed`.

> Prior to Apollo Client 3.3, `readFragment` would throw `MissingFieldError` exceptions to report missing fields, and return `null` only when reading a fragment from a nonexistent ID. Beginning with Apollo Client 3.3, `readFragment` always returns `null` to indicate insufficient data (missing ID or missing fields), instead of throwing a `MissingFieldError`.
> Prior to Apollo Client 3.3, `readFragment` threw `MissingFieldError` exceptions to report missing fields, and returned `null` only when reading a fragment from a nonexistent ID. Beginning with Apollo Client 3.3, `readFragment` always returns `null` to indicate insufficient data (missing ID or missing fields), instead of throwing a `MissingFieldError`.
### `writeFragment`

Expand Down Expand Up @@ -180,7 +190,7 @@ All subscribers to the Apollo Client cache (including all active queries) see th

## Combining reads and writes

Combine `readQuery` and `writeQuery` to fetch currently cached data and make selective modifications to it. The example below creates a new `Todo` item and adds it to your cached to-do list. Remember, this addition is _not_ sent to your remote server.
You can combine `readQuery` and `writeQuery` (or `readFragment` and `writeFragment`) to fetch currently cached data and make selective modifications to it. The example below creates a new `Todo` item and adds it to your cached to-do list. Remember, this addition is _not_ sent to your remote server.

```js
// Query that fetches all existing to-do items
Expand Down Expand Up @@ -219,7 +229,9 @@ client.writeQuery({
The `modify` method of `InMemoryCache` enables you to directly modify the values of individual cached fields, or even delete fields entirely.

* Like `writeQuery` and `writeFragment`, `modify` triggers a refresh of all active queries that depend on modified fields (unless you override this behavior).
* _Unlike_ `writeQuery` and `writeFragment`, `modify` circumvents any [`merge` functions](cache-field-behavior/#the-merge-function) you've defined, which means that fields are always overwritten with exactly the values you specify.
* _Unlike_ `writeQuery` and `writeFragment`:
* `modify` circumvents any [`merge` functions](cache-field-behavior/#the-merge-function) you've defined, which means that fields are always overwritten with exactly the values you specify.
* `modify` _cannot_ write fields that do not already exist in the cache.
* Watched queries can control what happens when they are invalidated by updates to the cache, by passing options like `fetchPolicy` and `nextFetchPolicy` to [`client.watchQuery`](../api/core/ApolloClient/#ApolloClient.watchQuery) or the [`useQuery`](../api/react/hooks/#options) hook.

### Parameters
Expand Down

0 comments on commit 37c0135

Please sign in to comment.