Before getting into the issue I just wanted to say what a great job you are doing with the apollo-client here at OpenTable we really are enjoying working with it!
We are currently using the apollo-client in a number of different use cases at it's working well 👍.
However, we have one application which is consuming the apollo-client via node-js and it is very memory sensitive (for various reasons) and because of this having the cache/store caching all queries and data is not great for us.
Because of this, we have a couple of solutions that we'd like to propose:
One possible way to disable caching is to specify a cachePolicy hash when creating the client within the networkInterface, with the default policy to always cache e.g.
js
const options = { uri: ..., cachePolicy: { query: true, data: false } };
const networkInterface = createBatchingNetworkInterface(options);
Advantages:
Disadvantages:
noFetch=true
with caching set to false would produce no results, which could be strange if the developer didn't have the context of what the policy was set to globally.The next natural alternative to controlling the cache policy is to do this at a query level, with the default policy to always cache e.g.
js
const restaurant = client.query({query: ..., cachePolicy: { query: true, data: false } });
Advantages:
Disadvantages:
Instead of disabling the caching we could set a byte limit for the cache size within the networkInterface e.g.
js
const options = { uri: ..., cacheSize: 10000 };
const networkInterface = createBatchingNetworkInterface(options);
Advantages:
From a personal point of view, I'd like to be able to control the cache on a per-query level whilst also being able to set the size of the cache.
What are your thoughts on this?
@chriscartlidge are you using Apollo Client in nodejs for server side rendering? If so, cache context should be garbage collected so long as you aren't keeping references to it after the render request. If you are simply trying to make server-to-server GraphQL requests, I would advise you to use something other than Apollo Client for that use case... probably just raw graphql-js
. Apollo Client is inherently designed to use with Redux
for a state-persisted single page apps, not for server-to-server requests (other than the case of server sider rendering of said apps)
Thanks for filing an issue, @chriscartlidge! This is definitely an interesting thought we haven't heard much about yet!
I'd love to find out more about the use case here, and the reasons you have selected Apollo for this case. At first glance, it doesn't sound like being able to control the caching on a per-query level is necessary for what you are doing, and disabling caching entirely is sufficient.
Some specific questions:
Hi guys, so what's the best way to disable cache on ApolloClient?
@phuochau I don't think disabling the cache is currently supported. I believe the intent behind apollo is to provide an opinionated graphql + caching solution for building client-side apps.
@chriscartlidge This is something we've been thinking about for a while. I'm curious about your specific use-case, because it might make more sense to simply fetch the GraphQL query "by hand" if you don't ever want to cache things and don't need results to be reactive (i.e. updating when newer results from overlapping queries come in).
Apollo Client currently uses the normalized cache to watch for updates to queries, which means that turning off the cache would make Apollo effectively just a fancy fetch function.
However, we have thought about this use-case, and our currently favored idea is the following:
cachePolicy
can be specified per-query. The are three cachePolicy
options:
normal
: the query result is stored in the cache indefinitely, but can be evicted if the cache grows beyond a certain size threshold.no-cache
: the query result is not stored in the cache.offline-critical
: the query result is stored in the cache indefinitely, and is not evicted at the normal threshold cache size. It is only evicted when the cache reaches a system resource-determined threshold, and only after all the data stored with the normal cachePolicy is evicted.Note that the no-cache
policy could only be used with query
, not watchQuery
unless we make some major changes to how Apollo Client delivers results.
If you're interested in this, we could get you started on implementing this in a PR! Does that sound like something you'd have time & motivation for?
@chriscartlidge @phuochau Disable cache? If you spend more time reading sources then writing issue, you will discover that you can use networkLayer
directly which effectively skip all caching. Yep, it is (in most cases) same as using fetch
or XMLHttpRequest
directly. If you want trigger new request and want it to be recorded back into Apollo store, then you should asking more precisely. But I think this is possible too.
@langpavel I don't think most people read through source code in their spare time. Some of us contribute more than others. That is to be expected, and I think everyone deserves respect :)
@thebigredgeek Reading sources is for me the easier way in case of Apollo :-) So sorry, I didn't want to be impolite :-)
Project I'm working on has a specific data structure which makes me unable to use dataIdFromObject
. Example: Updating an item (e.g. changing its status) moves it from one list(query) to another. Writing updateQueries
for every mutation is kinda verbose (although I have written a service for creating some generic update functions).
Optional disabling of cache is definitely a must-have.
@thebigredgeek @helfer @stubailo Thanks for the constructive replies and apologies for replying back to this late 👍
@stubailo Let me give you a bit of overview about our setup at OpenTable.
We currently have a front end structure where our front end is divided into self-contained microsites
e.g. the homepage & search page are 2 totally different applications.
As you can expect there is a use case of sharing frontend components
between these two sites e.g. headers, footers and other business related components.
For this, we are using something that we developed and open-sourced called OpenComponents which allows to build and share these components across microsites. These components are hosted on an OC-Registry and microsites can make a http
request where the component will then be server side rendered by the registry and sent back to the microsite in the response as html
.
We can have many components on a single page e.g. the header, restaurant-list and location picker. These components are responsible for fetching of their own data via calling our GraphQL API.
Our idea of using the apollo-client
was to leverage the batching and duplication it has to reduce the calls we make to the GraphQL API
and thus the underlying micro services that feed it.
This worked quite well for us at first as each instance of the registries had a singleton instance of the client which the components used to fetch their data and we had a lot of ROI on the batching/de-duplication cross components.
However, due to the amount of request that we have (double digit millions per day) for components the caching got out of hand as it kept growing and growing and thus the proposal I sent above.
We currently don't use pagination, mutation handling or polling but it is something we are very interested in doing in the coming months.
Note: We also use Apollo within our microsites for server-side render when components are not used.
I hope that's a decent overview of our current situation and why we have chosen the apollo-client for use with OpenComponents.
I like the suggestion that you have made @helfer and I'd like to explore helping out more on getting these supported within the client.
I am wondering if we could solve this, along with other requests for a more flexible caching structure, by implementing a sort of "pluggable" storage interface, as we do with NetworkInterface
, for the cache. This would be in lieu of direct coupling with Redux. I need to look more into how Apollo Client returns results into integration libs like react-apollo
before I can say confidently that this would work, though... because I am guessing some libs require that the entry be present in Redux
before they receive a value at all, so I am not sure how we could implement something like a "No-Op" cache if that is the case. @helfer I may have mentioned this to you before?
This would be nice. I'm working on a project using graphql and we will be doing queries with sensitive data that can't stay around in memory. Being able to disable cache for these queries would be great. I'd rather be able to use apollo for these instead of doing async calls myself.
@thebigredgeek I was doing exactly what you said not to here: https://github.com/apollographql/apollo-client/issues/1419#issuecomment-287274233
I'm using ApolloClient
on the server, just to use GraphQL, because I came to GraphQL through Apollo, so it was my default.
Presumably I don't need to use ApolloServer either, but just an executable schema?
I acomplished that, using directly networkInterface
:
@bind
loadMoreItems(offset, limit) {
const { client, loadMoreItemsGQLQuery } = this.props;
return client.networkInterface.query({
query: loadMoreItemsGQLQuery,
variables: {
offset,
limit
}
}).then((res) => {
const { data } = res;
_(data)
.values()
.first()
.map((item, i) => {
this.items.set(offset + i, item);
})
;
});
}
@maximblack I also managed to achieve this using a network-only fetch policy like so:
const { client } = this.props
client.query({
query: myGQLQuery,
fetchPolicy: 'network-only', // skip the cache
}).then(res => {
// done
})
Use the withApollo()
HOC to get a client injected into this.props
.
@maximblack @credli you might be interested in what we're working on over here: https://github.com/apollographql/apollo-fetcher/blob/44fe1084954882039d76edbbf921882577d8fb13/SPEC.md
This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!
If we can set fetchPolicy
to cache-and-network
globally, then we don't have to worry about invalidating the caches everywhere (refetch).
Could we have an option like this
const client = new ApolloClient({
defaultFetchPolicy: 'cache-and-network'
})
@vinhlh Yeah! That would be great!
Could you provide any help on how to use apollo-link
to bypass the cache?
I am also using apollo-client
with an application that has sensitive information, so would like to cache nothing on disk by default. Is there a way to do this?
Apollo Client doesn't cache on disk - it's just a JS variable, so when you close the page it's all gone.
This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!
For anyone else having this issue, I ended up creating a custom higher-order component that will prevent caching on the server while using the provided/cached value (and updating if necessary) once the client loads. I found this useful for ensuring that my isomorphic React app didn't run into an error because the client-generated HTML doesn't match the server-generated HTML.
You can use this in the same way you would use the graphql
higher-order component, just call this one instead.
Edit: Turns out this isn't actually working as I thought. The "network-only" value is still caching on the server. See here.
Does anyone have any suggestions for workarounds to disabling the cache until a feature like this is implemented?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!
I'm still interested in this.
@stubailo I would love to make a PR if you guys agree that we could have a defaultFetchPolicy
.
Can we have disable-cache per query. Right now I'm using client.resetStore(). But if there are multiple requests placed then it throws error Store reset while query in-flight.
Honestly, I found the best workaround to be to just call the network layer directly -- this was mentioned earlier in this thread. I've been doing that for the last few weeks and the only problem I've encountered is that deduplication doesn't work. Which is understandable because that is implemented at client level. The network layer still performs batching though, which is all I really need.
@vinhlh the new 2.0 will support defaultOptions
so you can set this!
@rrhvella any examples you know about that would demonstrate how to go about doing that?
@mhuggins I'm working on documentation for it this week! But you can check out these tests as a starting point!
@credli , @maximblack , to get props.client i must to send this "client"
const client = createClient({
uri: 'http://localhost:4000/graphql',
onError: error => {
console.log(error);
}
});
to my components, something like that ?
<ApolloProvider store={store} client={client}>
<BrowserRouter>
<Switch>
<MyComponent client={client}
(...)
I can too with this delete block code with 'update:' ? Eg in
const handleKeyUp = (evt) => {
if (evt.keyCode === 13) {
mutate({
variables: { name: evt.target.value },
optimisticResponse: {
addChannel: {
name: evt.target.value,
id: Math.round(Math.random() * -1000000),
__typename: 'Channel',
},
},
update: (store, { data: { addChannel } }) => {
// Read the data from the cache for this query.
const data = store.readQuery({ query: channelsListQuery });
// Add our channel from the mutation to the end.
data.channels.push(addChannel);
// Write the data back to the cache.
store.writeQuery({ query: channelsListQuery, data });
},
});
evt.target.value = '';
}
};
I think to use apollo client to manage millions of records , but i'm not sure if is the right tool, i'll use for small tables, but if i can deactivate the cache until wait version 2, it would be nice so I can manage cache for small tables , but not for bigtables. Anyway don't use cache for small tables is not really slow to work with Network directly, for me is useful apollo client to talk with apollo server graphql in a same language, so cache is not really important.
the solution i've found is just to set options in every gql:
http://dev.apollodata.com/react/api-queries.html#graphql-config-options-fetchPolicy
fetchPolicy: network-only
there is no global settings
Example:
const ComponentWithMutations = compose(
graphql(gqlsCustomer.gqlCustomerView, {
name: 'queryData',
options: props => ({
variables: { id: props.match.params.id },
fetchPolicy: 'network-only',
}),
}),
graphql(gqlsCustomer.gqlCustomerUpdate, {
name: 'mutationData',
options: props => ({
variables: { id: props.match.params.id },
fetchPolicy: 'network-only',
}),
}),
)(withCrudFormContainer);
@webmobiles Unfortunately that doesn't work as expected with react-apollo. That package explicitly converts "network-only" to "cache-first" when using server-side rendering. You can see a change I proposed here to see about removing this behavior.
@mhuggins I've tested it and is working for me...i'm not using server rending , it's react app front end app
Yes, the issue is specifically for when server rendering. Just adding a
warning here that your solution won't work for everyone. :)
On Oct 27, 2017 7:50 AM, "Dave I." notifications@github.com wrote:
@mhuggins https://github.com/mhuggins I've tested it and is working for
me...i'm not using server rending , it's react app front end app—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/apollo-client/issues/1419#issuecomment-339977066,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAKUhQ0CzKBrCxhNM4kqZmsmyRZ_MRYUks5swd-_gaJpZM4MfNbs
.
is it still possible to use apollo.networkinterface directly with apollo 2.0?
@rrhvella
@michaelknoch
I actually switched to using apollo-link directly: http://apollo-link-docs.netlify.com/docs/link/
this works like charm, thanks @rrhvella
example:
const query = operation => makePromise((execute(apolloClient.link, operation)));
query({ query, variables })
.then(result => {
console.log(result)
})
.catch(err => this.setErrorState(err));
I wondered why its not possible to create a separate apolloClient without cache.
It seems that apollo-client is explicitly depending on the cache object
const clientWithoutCache = new ApolloClient({
link: new HttpLink({ uri: 'http://api.githunt.com/graphql' }),
cache: null
});
How about a NullCache implementation? It would work akin to a mock cache, with support for fragmentMatcher and whatever else is needed, but would not actually store anything, and read operations would return NOT FOUND.
I just overwrote the query method:
const apolloQuery = client.query.bind(client)
client.query = async (...args) => {
await client.cache.reset()
return apolloQuery(...args)
}
EDIT:
Replaced client.resetStore()
with client.cache.reset()
.
Using client.resetStore()
throws something like ERROR: store reset while query was in flight
when queries are executed concurrently. client.cache.reset()
doesn't throw any errors and, perhaps more importantly, just resets the cache instead of the entire store. 😄
@OskarKaminski Dosn't seem to work. (apollo-client: 2.0.4)
I find creating an ObservableQuery using watchQuery and then refetch much more idiomatic.
@OskarKaminski fetchPolicy globally does not work, I must to do it one by one
I am also hitting a situation where the Apollo Cache seems to be far to aggressive and we are not done completing all of our mutations. Due to this we keep getting some stale data that is a property of an object causing 'bugs' currently during our refactor to utilize graphql more predominantly.
@webmobiles Your solution worked perfectly for me.
I'm running a React frontend with React-Tables that's using Server-side pagination. When I would go from the Table > Profile page > delete object > Redirect back to Table a new query was not being fired. fetchPolicy: 'network-only',
worked perfectly
fetchTableData = () => {
this.props.client.query({
query: this.props.fetchQuery,
fetchPolicy: 'network-only',
variables: {...},
}).then((res) => {...}).catch((res) => {...});
In the application I'm building there is the same need for cache on the frontend. It would be great to have something with the network layer to disable cache.
const client = new ApolloClient({
link: new HttpLink({ ... }),
cache: null,
});
That would be fantastic. I understand the importance of cache and I'm probably an edge case but would be super helpful to have something like that.
i have just read the apollo-client 2.2.2 changelog:
Add new fetchPolicy called 'no-cache' to bypass reading from or saving to the cache when making a query
has anyone tried it already?
Im using apolloClient.cache.data.clear()
here
As mentioned in https://github.com/apollographql/apollo-client/issues/1419#issuecomment-382877453, modern versions of apollo-client
now support a no-cache
fetchPolicy
option (using both query
and watchQuery
). This should address the issues outlined here, so I'll close this off. Happy to re-open if anyone disagrees. Thanks!
@hwillson this only disables the cache per query, but we need the option to disable the cache globally
@bogdansoare The apollo-client
constructor has a defaultOptions
param, that allows you to set an application wide fetchPolicy
of no-cache
. Is that good enough or would you prefer something different?
@hwillson understood. Will something like this work ?
const defaultOptions = {
watchQuery: {
fetchPolicy: 'no-cache',
errorPolicy: 'ignore'
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all'
},
mutate: {
errorPolicy: 'all'
}
};
@bogdansoare That should work - let us know if it doesn't!
@hwillson
const defaultOptions = {
watchQuery: {
fetchPolicy: 'no-cache',
errorPolicy: 'ignore'
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all'
},
}
Unfortunately that did't work for me. My queries return undefined
although when I check the network tab, it contains the actual data.
I'm using the <Query />
component
@paschaldev Any chance you could put together a small runnable reproduction that shows this happening? That would greatly help with troubleshooting. Thanks!
@hwillson I'll try to. For now I'll just go back to using the cache
To Disable Query Component Caching with react-apollo, you can use that;
<Query query={...} fetchPolicy="network-only" />
@maximblack I also managed to achieve this using a network-only fetch policy like so:
const { client } = this.props client.query({ query: myGQLQuery, fetchPolicy: 'network-only', // skip the cache }).then(res => { // done })
Use the
withApollo()
HOC to get a client injected intothis.props
.
Thanks its great we closed our issue
If you are using Nuxt, one strategy can be to write a plugin which wraps the query function on the default client, to turn off cache.
This code will yield an injected function $apolloQuery() which can be used in the app.
Create a plugin as ~/plugins/apollo-wrapper.js
:
export default ({ app }, inject) => {
inject('apolloQuery', async(params) => {
let result, errors
params.fetchPolicy = 'no-cache'
try {
result = await app.apolloProvider.defaultClient.query(params)
} catch (e) {
if (e.graphQLErrors && e.graphQLErrors.length) {
errors = apolloErrors(e.graphQLErrors)
} else {
errors = e.message
}
return { data: null, errors }
}
let data = Object.values(result.data)[0]
return { data, errors }
})
(...)
(implement: apolloErrors
, simple function that parses exceptions)
then in nuxt.config.js
:
plugins: [
{ src: '@/plugins/apollo-wrapper' },
this allows for you to call queries in your store such as:
// Contains GQL query
import GET_FOOBAR from '~/graphql/queries/getFoobar.gql'
(...)
export const actions = {
async getFoobar(context, payload) {
let { data, errors } = await this.$apolloQuery({
query: GET_FOOBAR,
variables: payload
})
if (data) {
return { foobar: data.foobar, errors: null }
} else {
return { foobar: null, errors }
}
},
Most helpful comment
@maximblack I also managed to achieve this using a network-only fetch policy like so:
Use the
withApollo()
HOC to get a client injected intothis.props
.