Apollo-client: FetchPolicy seems to be moderately broken - summary

Created on 14 May 2018  ·  34Comments  ·  Source: apollographql/apollo-client

Like the title states, fetchPolicy in apollo-client seems to be completely moderately broken.

I thought it would be a good idea to round up all of the issues in a central place:

no-cache:

  • [x] #3401 : update is not called in mutation when fetchPolicy set to 'no-cache'
  • [x] #3396 : Don't read from cache when fetchPolicy = 'no-cache'
  • [x] #3272 : Response without data when fetchPolicy: 'no-cache' isn't present (basically no-cache returns null)
  • [x] #3030 : Fix no-cache fetchPolicy returns null or undefined (this was merged in v2.2.4 but still a bug in v2.3.1)
  • [x] #2934 : support no-cache requests (breaking change)

cache-and-network:

  • #2119 : SSR and cache-and-network fetch policy
  • #3177 : fetchPolicy: 'cache-and-network' and client not fetching from the cache

cache-only:

  • #3343 : cache-only fetchPolicy: error not passed to Query component if query cannot be resolved from cache

network-only:

  • #1622 : Network-only refetch after network error does not update networkStatus

Misc:

  • #3243 : Refetch returned cached data
Issues related in React-Apollo repo:

network-only:

  • 556 : network-only returns cached value
  • 1895 : Fix network-only

cache-and-network:

  • 1342 : cache-and-network fetch policy on loading stage shows unrelated data
  • 1217 : Set network status to 3 with cache-and-network fetch policy

Personally l am experiencing all of the issues above in the latest version of apollo-client. It would be good to get these properly addressed as they are a pretty important part of apollo.

If anyone else has any more issues (closed or open) that can be linked into this, just comment below with it.

@evans and @jbaxleyiii Can we get this put as quite a high priority on the apollo-client Roadmap, if help is need, l can also look into these on the weekend

Version

Issue Labels

  • [x] has-reproduction
  • [x] blocking
  • [x] bugs
🚧 in-triage

Most helpful comment

Using fetchPolicy=no-cache for me also results in data: {}, even though the network response is present and visible.

Specifically, I end up with:

loading: false,
error: undefined,
data: {},
...

when using <Query fetchPolicy="no-cache" /> component .

   // package.json
    "apollo-cache-inmemory": "1.2.1",
    "apollo-client": "2.3.1",
    "apollo-codegen": "0.19.1",
    "apollo-link": "1.2.2",
    "apollo-link-error": "1.0.9",
    "apollo-link-http": "1.5.4",
    "react-apollo": "2.1.4"

Here is a reproduction, showing how the data is missing:
https://codesandbox.io/s/4zmopq25o0

It also demonstrates different results when defaultOptions versus setting the fetchPolicy component prop is used...

Related: Using defaultOptions with a fetchPolicy for query does not seem to work - I get different behavior using defaultOptions: { query : { fetchPolicy: "no-cache" } } and <Query fetchPolicy="no-cache" />

This seems to be supported according to tests and docs

All 34 comments

Thanks for putting this list together @OllieJennings! I've been working through all outstanding apollo-client issues, and will be getting to these shortly.

if help is need, l can also look into these on the weekend

Help is 💯% always welcome! If you're able to chip in by reviewing these issues and adding your comments, helping with reproductions, or better yet, working on PR's, that would be awesome!

Using fetchPolicy=no-cache for me also results in data: {}, even though the network response is present and visible.

Specifically, I end up with:

loading: false,
error: undefined,
data: {},
...

when using <Query fetchPolicy="no-cache" /> component .

   // package.json
    "apollo-cache-inmemory": "1.2.1",
    "apollo-client": "2.3.1",
    "apollo-codegen": "0.19.1",
    "apollo-link": "1.2.2",
    "apollo-link-error": "1.0.9",
    "apollo-link-http": "1.5.4",
    "react-apollo": "2.1.4"

Here is a reproduction, showing how the data is missing:
https://codesandbox.io/s/4zmopq25o0

It also demonstrates different results when defaultOptions versus setting the fetchPolicy component prop is used...

Related: Using defaultOptions with a fetchPolicy for query does not seem to work - I get different behavior using defaultOptions: { query : { fetchPolicy: "no-cache" } } and <Query fetchPolicy="no-cache" />

This seems to be supported according to tests and docs

I am not sure about this one it is just my quick observation but it seems that when using withApollo calling query with no-cache does not work at all it always updates the cache.

meet the same issue

I also had an issue with apollo-client being used in conjunction with apollo-link-state.

If I do not explicitly specify a 'no-cache' fetchPolicy on the for local state, I would get stale data on page change despite proper updates on the component before page change.

fetchPolicy: "no-cache" still seems to be broken

    "apollo-cache-inmemory": "1.2.5",
    "apollo-client": "2.3.5",
    "apollo-link-batch-http": "1.2.2",
    "apollo-link-error": "1.0.9",
    "apollo-link-http": "1.5.4",
    "react-apollo": "2.1.8",

same here::

    "apollo-boost": "^0.1.1",
    "apollo-cache-inmemory": "^1.1.9",
    "apollo-client": "^2.3.5",
    "apollo-link-context": "^1.0.7",
    "apollo-link-http": "^1.5.1",

Has anyone found a decent workaround for disabling the auto caching?

We added a resolver that is a "cache buster" so that we send unique queries every time since the arguments are different given a unique id (using the uuid package). This only works if you can control the schema though.

query Document($documentId: String!, $uniqueId: String) {
   # Original query
  document(ID: $documentId) { ... }
  # This resolver just returns true and is used so that our query always has a unique arg
  cacheBuster(uniqueID: $uniqueId)
}
import uuid from 'uuid';
...
<Query query={documentQuery} variables={{ documentId: "1234", uniqueId: uuid.v4() }}

@kristiehowboutdat wouldn't this also fill up your cache unnecessarily? Given the cache isn't a proper cache (with an LRU) this will cause your app's memory demand to increase.

Yeah @jync, there are a couple of downsides, including that. It's a workaround, not a solution 😞 For our app we cannot have cached values, and using "no-cache" was not giving us the values from the server and causing lots of stale data to be displayed - this at least solved that. Curious how others have been working around this.

Would welcome suggestions on how to avoid filling the cache at all or clearing it. Last time I looked at the docs (1-2 months ago), I didn't find a good way to programmatically clear the cache.

When I don't set the defaultOptions for ApolloClient, it uses the 'cache-first' as expected, but when I set it explicit with the defaultOptions it seems to use 'network-only' even If I have it set to 'cache-first'

example can be found here https://codesandbox.io/s/jjn4pnnz7v

Any workaround for this? Seems like apollo client is not usable because of this issue!

@fbartho In a project in which I dont use apollo-link-state I have the same issues. The most important for me is that I have 1 view with many filters and pagination and caching is pointless there, but with cache-policy: 'no-cache' no data is rendered, despite the fact that in browser devtools I can see that response has data in it.

Hi all - I'll be tackling this issue in chunks, since there is a lot covered here. First up is dealing with any outstanding no-cache issues. I've created a new temporary label to identify outstanding no-cache related issues; if anyone knows of issues I've missed with the label, please let me know. Thanks!

All no-cache related items listed here should now be resolved. If anyone notices any additional no-cache issues, please let me know.

@kristiehowboutdat Thanks very much for you repro in https://github.com/apollographql/apollo-client/issues/3452#issuecomment-389601457. It really helped narrow down the issue. Just so you know, your reproduction is actually demonstrating 2 separate issues. The first problem is the fact that passing a no-cache fetch policy directly into your <Query /> component is broken (which is now fixed by https://github.com/apollographql/apollo-client/pull/3777 and will be released soon). The second problem where you mentioned setting a no-cache fetch policy in defaultOptions is being ignored, is actually working as intended. React Apollo uses Apollo Client's watchQuery functionality, instead of its query functionality. To set defaultOptions properly when using React Apollo, you need to use something like:

const client = new ApolloClient({
  ...
  defaultOptions: {
    watchQuery: {
      fetchPolicy: "no-cache"
    }
  }
});

Now of course we don't have this documented anywhere, because we like to keep developers guessing ... 🤦‍♂️.

I'll make sure this gets added to the docs - thanks!

Thanks @hwillson for your fix.

I tested the 2.4.0-alpha.14 release of apollo-client in my project. The issue still seems to occur when trying to load data with fetchPolicy "no-cache", but only sometimes.

I tried to debug my example and it looks like the query can resolve the data, but immediately removes it again.

1. fetch data --> { data: undefined, loading: true }
2. data was resolved successfully --> { data: [....], loading: false }
3. data was removed --> { data: undefined, loading: false, error: undefiend }

Anyone else experiencing this on 2.4.0-alpha.14?

@michaelknoch Can you try the recently released apollo-client 2.3.8 instead? Thanks!

the error still occurs, but only once in 5 app launches @hwillson

I'm facing the same random issue as @michaelknoch. Not sure, but I found that if I delay the response from the server it will never return undefined but the actual data.

To test I'm using bluebird on the server with something like...

require('bluebird').delay(1000, { foo: 'bar' })

@michaelknoch @hwillson i am getting the same issue as well. I am using 2.4.0. Occasionally it will work as expected, when it doesn't I can see the query keeps running periodically. The second and every query after that is returning an empty object.

I'm having an issue with the optimistic response. When fetch-policy="cache-first" optimistic response works flawless. But when its cache-and-network it doesn't work most time, and sometimes it just works.

All no-cache related items listed here should now be resolved. If anyone notices any additional no-cache issues, please let me know.

@kristiehowboutdat Thanks very much for you repro in #3452 (comment). It really helped narrow down the issue. Just so you know, your reproduction is actually demonstrating 2 separate issues. The first problem is the fact that passing a no-cache fetch policy directly into your <Query /> component is broken (which is now fixed by #3777 and will be released soon). The second problem where you mentioned setting a no-cache fetch policy in defaultOptions is being ignored, is actually working as intended. React Apollo uses Apollo Client's watchQuery functionality, instead of its query functionality. To set defaultOptions properly when using React Apollo, you need to use something like:

const client = new ApolloClient({
  ...
  defaultOptions: {
    watchQuery: {
      fetchPolicy: "no-cache"
    }
  }
});

Now of course we don't have this documented anywhere, because we like to keep developers guessing ... 🤦‍♂️.

I'll make sure this gets added to the docs - thanks!

Using this approach:

const client = new ApolloClient({
  uri: 'https://graphql-pokemon.now.sh/',

  // Disable caching
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'no-cache'
    }
  }
})

causes Console warning:

ApolloBoost was initialized with unsupported options: defaultOptions

any progress on this? Im still running into the weird issue mentioned on August 9

Hey @hwillson I am seeing the same issue as @SergeyVolynkin - your suggested fix does not work when importing ApolloClient from apollo-boost - it causes a warning: ApolloBoost was initialized with unsupported options: defaultOptions.

This is really frustrating ... if Apollo caching doesn't work, it really reduces the performance of Query components since the whole argument that it's OK to use them declaratively is that "it will load cached data first". If it's always going to fail in the cache and just load the network response, then Query components are a lot less performant than just doing the query ourselves in componentDidMount and caching ourselves, but then we have to do a bunch of state management ourselves ...

Does caching work if I setup the ApolloClient myself, i.e. do not use apollo boost?

We use apollo-client 2.4.1 and experienced issues with no-cache:

From what we could gather, for no apparent reason, once every few times (less than 10 in our case) the Query component will trigger twice when fetchPolicy is set to no-cache. The first invocation will have the data, the second invocation will not.

We have replaced all our "no-cache" policies with "network-only" and will continue monitoring.

@rajjejosefsson

When I don't set the defaultOptions for ApolloClient, it uses the 'cache-first' as expected, but when I set it explicit with the defaultOptions it seems to use 'network-only' even If I have it set to 'cache-first'

example can be found here https://codesandbox.io/s/jjn4pnnz7v

The react Query component uses watchQuery and not query. If you change the defaultOption of watchQuery to 'cache-first' then it works as expected.

I have fixed your example here https://codesandbox.io/s/68q5p5vow

Yeah @jync, there are a couple of downsides, including that. It's a workaround, not a solution 😞 For our app we cannot have cached values, and using "no-cache" was not giving us the values from the server and causing lots of stale data to be displayed - this at least solved that. Curious how others have been working around this.

Would welcome suggestions on how to avoid filling the cache at all or clearing it. Last time I looked at the docs (1-2 months ago), I didn't find a good way to programmatically clear the cache.

I am using withApollo() and no-cache does not work for me either.

I ended up with a workaround using client.resetStore() directly after a mutation to empty the cache.

I was able to reproduce it within the original apollo codesandbox example by setting the fetchPolicy to "no-cache" https://codesandbox.io/s/k9y4v0wlov.

Due to some experiments I think this issue is a bug in react-apollo. The watchquery itself seems to work as expected, but maybe im wrong

It appears https://github.com/apollographql/apollo-client/pull/4352 introduced a new bug related to fetch-policy: 'no-cache'. The original QueryManager code would would call this.dataStore.getCache().read() inside getCurrentQueryResult(), which would throw an exception and return { data: undefined, partial: true };, which would get detected here: https://github.com/danilobuerger/apollo-client/blob/b200a054d955daaf199aa052ee4d1d7ce8badbc7/packages/apollo-client/src/core/ObservableQuery.ts#L216-L218 as loading: true. After the change, getCurrentQueryResult() now return { data: undefined, partial: false }; and so a call to getCurrentResult() would return { loading: false, data: undefined, networkStatus: 7 }; Indicating that all data had been returned successfully, even though queryLoading was actually true and a query was actually being rendered.

Since a lot of these issues have been fixed, let's open issues related to specific problems instead of one bulk one! Thank you!

I have a similar problem.
I made a search cancellation like this.
However, there is a problem that 'words that have already been canceled' are not recalled.

EX)
If "apple" is cancelled, it searches up to "appl",
but "apple" does not re call api.

  • I don't speak English, so I use Google Translate.

[process]
A. Whenever a new event occurs, cancel the previous abortController.
B. Reassign a new abortController.

[reason]

  • abortController.abort() only worked the first time.
  • because abortController only looks at the 'first called API'.
  • So, every time I call the API, I allocate abortController again.

스크린샷 2020-10-16 오후 3 29 10

[logic1]

   const [_controller, set_controller] = useState(new AbortController());   // state

[logic2]

      const { GET_MY_NAME } = query;
      const [getMyData] = useLazyQuery(GET_MY_NAME, {
        fetchPolicy: "no-cache",
        variables: { myword: myword },
        context: {
          fetchOptions: {
            signal: _controller.signal
          }
        },

        onCompleted: (data) => {
            // get data!!    ex) dispatch(nameActions.setName(data.name));
        }
     });

[logic3]

  let timer = null;

  const onChange = (e) => {
     // 1. kill old API
      _controller.abort();  

      // 2. Initialize setTimeout when new input comes in
      clearTimeout(timer);  

     // 3. state update, API start
      timer = setTimeout(async (_) => {
          await set_controller(new AbortController());  
          await getMyData();
      }, 500);
  }

[logic4]

          <input
            onChange={onChange}
          />

[Method currently being considered]

I am thinking of a method of separating components only in the search box and fetching components whenever they are called...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dispix picture dispix  ·  3Comments

stubailo picture stubailo  ·  3Comments

stubailo picture stubailo  ·  3Comments

helfer picture helfer  ·  3Comments

MichaelDeBoey picture MichaelDeBoey  ·  3Comments