Skip to content

Commit

Permalink
Re-arrange the local state section
Browse files Browse the repository at this point in the history
Split the local state section up into separate pages, to make it
easier to read through related concepts. This also helps ensure
the right hand menu can help show relevant subsections, that were
previously buried under level 3 and 4 titles.
  • Loading branch information
hwillson committed Jun 23, 2020
1 parent 566a5ce commit 1791433
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 315 deletions.
5 changes: 4 additions & 1 deletion docs/gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ module.exports = {
],
'Local State': [
'local-state/local-state-management',
'local-state/field-policies-reactive-vars',
'local-state/querying-and-updating',
'local-state/reactive-variables',
'local-state/managing-state-with-field-policies',
'local-state/client-side-schema',
'local-state/local-resolvers'
],
'Development & Testing': [
Expand Down
2 changes: 1 addition & 1 deletion docs/source/caching/cache-field-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const cache = new InMemoryCache({
});
```

> Note that to query for a field that is only defined locally, your query should [include the `@client` directive](../local-state/field-policies-reactive-vars/#querying-local-state) on that field so that Apollo Client doesn't include it in requests to your GraphQL server.
> Note that to query for a field that is only defined locally, your query should [include the `@client` directive](../local-state/querying-and-updating#querying-local-state) on that field so that Apollo Client doesn't include it in requests to your GraphQL server.
Other use cases for a `read` function include:

Expand Down
37 changes: 37 additions & 0 deletions docs/source/local-state/client-side-schema.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Client-side schema
description: Configure a client-side schema with Apollo Client
---

You can optionally set a client-side schema to be used with Apollo Client, through either the `ApolloClient` constructor `typeDefs` parameter, or the local state API `setTypeDefs` method. Your schema should be written in [Schema Definition Language](https://www.apollographql.com/docs/graphql-tools/generate-schema#schema-language). This schema is not used for validation like it is on the server because the `graphql-js` modules for schema validation would dramatically increase your bundle size. Instead, your client-side schema is used for introspection in [Apollo Client Devtools](https://github.com/apollographql/apollo-client-devtools), where you can explore your schema in GraphiQL.

The following demonstrates how to configure a client-side schema through the `ApolloClient` constructor:

```js
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const typeDefs = gql`
extend type Query {
isLoggedIn: Boolean!
cartItems: [Launch]!
}
extend type Launch {
isInCart: Boolean!
}
extend type Mutation {
addOrRemoveFromCart(id: ID!): [Launch]
}
`;

const client = new ApolloClient({
cache: new InMemoryCache(),
uri: 'http://localhost:4000/graphql',
typeDefs,
});
```

If you open up Apollo Client Devtools and click on the `GraphiQL` tab, you'll be able to explore your client schema in the "Docs" section. This example doesn't include a remote schema, but if it did, you would be able to see your local queries and mutations alongside your remote ones.

![GraphiQL Console](../assets/client-schema.png)
114 changes: 2 additions & 112 deletions docs/source/local-state/local-resolvers.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Local resolvers
title: Local resolvers (DEPRECATED)
description: Manage local data with GraphQL like resolvers
---

> ⚠️ **DEPRECATION WARNING:** Local resolvers are still supported in Apollo Client 3, but they have been deprecated. We recommend using [field policies and reactive variables](./field-policies-reactive-vars) instead. Local resolver support will be removed in a future major Apollo Client release. Refer to [Local resolver deprecation](./local-state-management#local-resolver-deprecation) for more details.
> ⚠️ **DEPRECATION WARNING:** Local resolvers are still supported in Apollo Client 3, but they have been deprecated. We recommend using [field policies](./managing-state-with-field-policies) instead. Local resolver support will be removed in a future major Apollo Client release. Refer to [Local resolver deprecation](./local-state-management#local-resolver-deprecation) for more details.
We've learned how to manage remote data from our GraphQL server with Apollo Client, but what should we do with our local data? We want to be able to access boolean flags and device API results from multiple components in our app, but don't want to maintain a separate Redux or MobX store. Ideally, we would like the Apollo cache to be the single source of truth for all data in our client application.

Expand All @@ -13,8 +13,6 @@ In this section, you'll learn how Apollo Client can help simplify local state ma

Please note that this documentation is intended to be used to familiarize yourself with Apollo Client's local state management capabilities, and serve as a reference guide. If you're looking for a step by step tutorial outlining how to handle local state with Apollo Client (and leverage other Apollo components to build a fullstack application), please refer to the [Apollo tutorial](https://www.apollographql.com/docs/tutorial/introduction).

> ⚠️ If you're interested in integrating local state handling capabilities with Apollo Client < 2.5, please refer to our (now deprecated) [`apollo-link-state`](https://github.com/apollographql/apollo-link-state) project. As of Apollo Client 2.5, local state handling is baked into the core, which means it is no longer necessary to use `apollo-link-state`. For help migrating from `apollo-link-state` to Apollo Client 2.5, please refer to the [Migrating from `apollo-link-state`](#migrating-from-apollo-link-state) section.
## Updating local state

There are two main ways to perform local state mutations. The first way is to directly write to the cache by calling `cache.writeQuery`. Direct writes are great for one-off mutations that don't depend on the data that's currently in the cache, such as writing a single value. The second way is by leveraging the `useMutation` hook with a GraphQL mutation that calls a local client-side resolver. We recommend using resolvers if your mutation depends on existing values in the cache, such as adding an item to a list or toggling a boolean.
Expand Down Expand Up @@ -924,41 +922,6 @@ const client = new ApolloClient({
In order to toggle our todo, we need the todo and its status from the cache, which is why we call `cache.readFragment` and pass in a fragment to retrieve it. The `id` we're passing into `cache.readFragment` refers to its cache key. If you're using the `InMemoryCache` and not overriding the `dataIdFromObject` config property, your cache key should be `__typename:id`.
## Client-side schema
You can optionally set a client-side schema to be used with Apollo Client, through either the `ApolloClient` constructor `typeDefs` parameter, or the local state API `setTypeDefs` method. Your schema should be written in [Schema Definition Language](https://www.apollographql.com/docs/graphql-tools/generate-schema#schema-language). This schema is not used for validation like it is on the server because the `graphql-js` modules for schema validation would dramatically increase your bundle size. Instead, your client-side schema is used for introspection in [Apollo Client Devtools](https://github.com/apollographql/apollo-client-devtools), where you can explore your schema in GraphiQL.
The following demonstrates how to configure a client-side schema through the `ApolloClient` constructor:
```js
import { ApolloClient, InMemoryCache, HttpLink, gql } from '@apollo/client';

const typeDefs = gql`
extend type Query {
isLoggedIn: Boolean!
cartItems: [Launch]!
}
extend type Launch {
isInCart: Boolean!
}
extend type Mutation {
addOrRemoveFromCart(id: ID!): [Launch]
}
`;

const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({ uri: 'http://localhost:4000/graphql' }),
typeDefs,
});
```
If you open up Apollo Client Devtools and click on the `GraphiQL` tab, you'll be able to explore your client schema in the "Docs" section. This example doesn't include a remote schema, but if it did, you would be able to see your local queries and mutations alongside your remote ones.
![GraphiQL Console](../assets/client-schema.png)
## Advanced
### Code splitting
Expand Down Expand Up @@ -1023,79 +986,6 @@ export function MessageCount() {
our local resolver code will only be included in the bundle a user downloads when (if) they access `/stats`. It won't be included in the initial application bundle, which helps keep the size of our initial bundle down, and ultimately helps with download and application startup times.
## Migrating from `apollo-link-state`
The [`apollo-link-state`](https://github.com/apollographql/apollo-link-state) project was the first to bring local state handling into the Apollo ecosystem. Handling local resolvers through the addition of an `ApolloLink` was a great starting point, and proved that `@client` based queries make sense, and work really well for local state management.
While `apollo-link-state` achieved some of the goals of local state handling, the information available when using any `ApolloLink` is limited by the modularity of the link system. We consider local state management a core part of the Apollo ecosystem, and as Apollo Client progresses, we want to make sure local resolvers are integrated as tightly as possible into core. This integration opens up new possibilities (like `@export` handling) and ties nicely into the future planned adjustments to cache data retention, invalidation, garbage collection, and other planned features that impact both local and remote data.
Updating your application to use Apollo Client's local state management features, instead of `apollo-link-state`, is fairly straightforward. The necessary steps are outlined below.
1. Including `apollo-link-state` as a dependency, and importing it to use `withClientState`, is no longer necessary. You can remove the `apollo-link-state` dependency since local state management is included with `apollo-client` >= 2.5.0 and `@apollo/client` >= 3.0.0.
2. Using `withClientState` is no longer supported. The following:
```js
const cache = new InMemoryCache();
const stateLink = withClientState({ cache, resolvers: { ... } });
const link = ApolloLink.from([stateLink, new HttpLink({ uri: '...' })]);
const client = new ApolloClient({
cache,
link,
});
```
becomes
```js
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({ uri: '...' }),
resolvers: { ... },
});
```
3. `defaults` are no longer supported. To prep the cache, use [`cache.writeQuery`](../caching/cache-interaction/#writequery-and-writefragment) directly instead. So
```js
const cache = new InMemoryCache();
const stateLink = withClientState({
cache,
resolvers: { ... },
defaults: {
someField: 'some value',
},
});
const link = ApolloLink.from([stateLink, new HttpLink({ uri: '...' })]);
const client = new ApolloClient({
cache,
link,
});
```
becomes:
```js
const cache = new InMemoryCache();
const client = new ApolloClient({
cache,
link: new HttpLink({ uri: '...' }),
resolvers: { ... },
});
cache.writeQuery({
query: gql`query GetSomeField { someField }`,
data: { someField: 'some value' },
});
```
4. Test thoroughly! 🙂
## Next steps
Managing your local data with Apollo Client can help simplify your state management code, since the Apollo cache becomes your single source of truth for all of the data in your application. If you'd like to learn more about Apollo Client's local state features, check out:
- The [Apollo tutorial](https://www.apollographql.com/docs/tutorial/introduction) which will not only show you how to use Apollo Client's local state features in a step by step manner, but will also guide you through using other Apollo components to build a fullstack application.
- The [Apollo community Spectrum group](https://spectrum.chat/apollo) is a great place to ask Apollo Client local state questions.
- Interested in suggesting or working on future changes to help make Apollo Client's local state management even better? We'd love the help! [Open a new feature request](https://github.com/apollographql/apollo-feature-requests) to kick start your feature discussion.
- Found a bug? Impossible! 🙈 Open a new issue in the [Apollo Client repo](https://github.com/apollographql/apollo-client), ideally with a small runnable reproduction, and someone from the community or Apollo team will help get it fixed.
## API
Apollo Client local state handling is baked in, so you don't have to install anything extra. Local state management can be configured during `ApolloClient` instantiation (via the `ApolloClient` constructor) or by using the `ApolloClient` local state API. Data in the cache can be managed through the `ApolloCache` API.
Expand Down
8 changes: 5 additions & 3 deletions docs/source/local-state/local-state-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ description: Interact with your local data in Apollo Client

We've learned how to manage remote data from our GraphQL server with Apollo Client, but what should we do with our local data? We want to be able to access boolean flags and device API results from multiple components in our app, but don't want to maintain a separate Redux or MobX store.

Apollo Client provides built-in local state handling capabilities that allow you to access local data by just querying it with GraphQL. You can even request local and server data within the same query. Apollo Client currently supports two approaches for managing local state.
Apollo Client provides built-in local state handling capabilities that allow you to access local data by just querying it with GraphQL. You can even request local and server data within the same query. All of Apollo Client's local state management features are driven by its caching system, which serves as its foundation. [Interacting with cached data](../caching/cache-interaction/) covers how to directly read from and write into the cache, along with other cache API features. It's a good idea to read over this section before continuing with the local state docs.

The Apollo Client cache API can be seamlessly integrated into client side applications, to streamline, simplify and enhance client side data graph management. To accomplish this, Apollo Client currently supports two approaches for managing local state.

**1. Field policies and reactive variables**

Expand All @@ -15,7 +17,7 @@ Field policies provide a way to customize the reading and writing of individual

Reactive variables are functions that allow you to store local state outside of the cache. Field policies can use them to combine local state with queries read from the cache, but they can also be referenced and updated directly anywhere in your application. Changes made to reactive variables are internally broadcast within Apollo Client, allowing watched queries that use reactive variables to be automatically updated with the newly changed data.

Field policies and reactive variables can be combined in powerful ways to drive an effective client side state management system. Refer to [Field policies and reactive variables](./field-policies-reactive-vars) for more details and local state specific examples.
Field policies and reactive variables can be combined in powerful ways to drive an effective client side state management system. Refer to the [reactive variables](./reactive-variables) and [managing state with field policies](./managing-state-with-field-policies) sections for more details and local state specific examples.

**2. Local resolvers**

Expand All @@ -31,4 +33,4 @@ The idea of using client side resolvers to manage local state was first introduc

To help address limitations in the local resolver API, we have designed and implemented a new approach for managing local state in Apollo Client 3.0, that works as a direct extension of the cache. Field policies and reactive variables not only help provide a better developer experience from an API use and functionality point of view, but they also improve performance and provide a more reliable foundation for local state management. Re-thinking local state handling with the Apollo Client cache in mind has helped reduce a large number of local state bugs caused by local resolvers being a few too many layers removed from the cache internals.

The [Field policies and reactive variables](./field-policies-reactive-vars) section goes into more detail around what Apollo Client 3's new local state management capabilities look like. We highly recommend reviewing and considering the use of these new API's as a replacement for local resolvers. Local resolvers are still supported in Apollo Client 3, but should be considered deprecated. Local resolver functionality will be removed in a future major version of Apollo Client.
The [managing state with field policies](./managing-state-with-field-policies) section goes into more detail around what Apollo Client 3's new local state management capabilities look like. We highly recommend reviewing and considering the use of these new API's as a replacement for local resolvers. Local resolvers are still supported in Apollo Client 3, but should be considered deprecated. Local resolver functionality will be removed in a future major version of Apollo Client.
Loading

0 comments on commit 1791433

Please sign in to comment.