Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Manage your local data with Apollo Client!

Docs | Announcement Post | Tutorial Video by Sara Vieira

Managing remote data from an external API is simple with Apollo Client, but where do we put all of our data that doesn't fit in that category? Nearly all apps need some way to centralize client-side data from user interactions and device APIs.

In the past, Apollo users stored their application's local data in a separate Redux or MobX store. With apollo-link-state, you no longer have to maintain a second store for local state. You can instead use the Apollo Client cache as your single source of truth that holds all of your local data alongside your remote data. To access or update your local state, you use GraphQL queries and mutations just like you would for data from a server.

When you use Apollo Client to manage your local state, you get all of the same benefits you know and love like caching and offline persistence without having to set these features up yourself. 🎉 On top of that, you also benefit from the Apollo DevTools for debugging and visibility into your store.

Quick start

To get started, install apollo-link-state from npm:

npm install apollo-link-state --save

The rest of the instructions assume that you have already set up Apollo Client in your application. After you install the package, you can create your state link by calling withClientState and passing in a resolver map. A resolver map describes how to retrieve and update your local data.

Let's look at an example where we're using a GraphQL mutation to update whether our network is connected with a boolean flag:

import{withClientState}from'apollo-link-state';// This is the same cache you pass into new ApolloClientconstcache=newInMemoryCache(...);conststateLink=withClientState({
cache,resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{constdata={networkStatus: {__typename: 'NetworkStatus',
isConnected
},};cache.writeData({ data });returnnull},},}});

To hook up your state link to Apollo Client, add it to the other links in your Apollo Link chain. Your state link should be near the end of the chain, so that other links like apollo-link-error can also deal with local state requests. However, it should go before HttpLink so local queries and mutations are intercepted before they hit the network. It should also go before apollo-link-persisted-queries if you are using persisted queries. Then, pass your link chain to the Apollo Client constructor.

constclient=newApolloClient({
cache,link: ApolloLink.from([stateLink,newHttpLink()]),});

How do we differentiate a request for local data from a request that hits our server? In our query or mutation, we specify which fields are client-only with a @client directive. This tells our network stack to retrieve or update the data in the cache with our resolver map that we passed into our state link.

constUPDATE_NETWORK_STATUS=gql` mutation updateNetworkStatus($isConnected: Boolean) { updateNetworkStatus(isConnected: $isConnected) @client }`;

To fire off the mutation from your component, bind your mutation to your component via your favorite Apollo view layer integration just like you normally would. Here's what this would look like for React:

constWrappedComponent=graphql(UPDATE_NETWORK_STATUS,{props: ({ mutate })=>({updateNetworkStatus: isConnected=>mutate({variables: { isConnected }}),}),})(NetworkStatus);

What if we want to access our network status data from another component? Since we don't know whether our UPDATE_NETWORK_STATUS mutation will fire before we try to access the data, we should guard against undefined values by providing a default state as part of the state link initialization:

conststateLink=withClientState({
cache,resolvers: {Mutation: {/* same as above */},},defaults: {networkStatus: {__typename: 'NetworkStatus',isConnected: true,}},});

This is the same as calling writeData yourself with an initial value:

// Same as passing defaults abovecache.writeData({networkStatus: {__typename: 'NetworkStatus',isConnected: true,}});

How do we query the networkStatus from our component? Similar to mutations, just use a query and the @client directive! With Apollo Link, we can combine data sources, including your remote data, in one query.

In this example, the articles field will either hit the cache or fetch from our GraphQL endpoint, depending on our fetch policy. Since networkStatus is marked with @client, we know that this is local data, so it will resolve from the cache.

constGET_ARTICLES=gql` query { networkStatus @client { isConnected } articles { id title } }`;

To retrieve the data in your component, bind your query to your component via your favorite Apollo view layer integration just like you normally would. In this case, we'll use React as an example. React Apollo will attach both your remote and local data to props.data while tracking both loading and error states. Once the query returns a result, your component will update reactively. Updates to Apollo Client state via apollo-link-state will also automatically update any components using that data in a query.

constWrappedComponent=graphql(GET_ARTICLES,{props: ({data: { networkStatus, articles, loading, error }})=>{if(loading){return{ loading };}if(error){return{ error };}return{
loading,
networkStatus,
articles,};},})(Articles);

For more detailed examples, plus in-depth explanations of resolvers, defaults, and more, please check out our official docs page.

With Apollo Boost

If you are using apollo-boost, it already includes apollo-link-state underneath the hood for you. Instead of passing the link property when instantiating Apollo Client, you pass in clientState.

importApolloClientfrom'apollo-boost';constclient=newApolloClient({clientState: {defaults: {isConnected: true},resolvers: {Mutation: {updateNetworkStatus: (_,{ isConnected },{ cache })=>{cache.writeData({data: { isConnected }});returnnull;}}}}});

Local Development

If you're setting up for local development, and you want to integrate a local branch of apollo-link-state into another application, remember that this project is a Lerna monorepo: ./packages/apollo-link-state

To link this in, do:

cd packages/apollo-link-state && yarn link

And in your development application do:

yarn link apollo-link-state

Finally, each time you make a change in apollo-link-state, you need to run:

yarn build && yarn bundle

Now you should be good to go!

About

✨ Manage your application's state with Apollo!

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages