Intended outcome:
Run a query and receive non-null data when using fetchPolicy: 'no-cache'
. Tested on Node and in Browser.
client.query({query, fetchPolicy: 'no-cache'})
Actual outcome:
The data
property is null
:
{
data: null
loading: false
networkStatus: 7
stale: true
}
How to reproduce the issue:
data: {...}
). (expected)no-cache
fetch policy: the Promise will eventually resolve with no data (data: null
). (unexpected)network-only
fetch policy: the Promise will eventually resolve with good data. (expected)clientA
works with default fetch policy: returns non-null data. (expected)clientB
does not work with no-cache
fetch policy: returns null
data. (unexpected)clientC
works with default fetch policy (returns non-null data), then the query is repeated again with clientC
, this time with no-cache
fetch policy, and the data returns non-null! (what?)Version
Contents of the runkit for reference
require('isomorphic-fetch')
require('graphql')
const { ApolloClient } = require('apollo-client')
const { HttpLink } = require('apollo-link-http')
const { InMemoryCache } = require('apollo-cache-inmemory')
const gql = require('graphql-tag')
const uri = 'https://graphql-pokemon.now.sh/graphql'
const query = gql`{
pokemon(name: "Pikachu") {
id
number
name
}
}`
const client = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
})
client.query({query})
.then(data => console.log(data))
.catch(error => console.error(error))
// => gives good data
const clientNoCache = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
})
clientNoCache.query({query, fetchPolicy: 'no-cache'})
.then(data => console.log(data))
.catch(error => console.error(error))
// => gives NULL data
const clientNetworkOnly = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
})
clientNoCache.query({query, fetchPolicy: 'network-only'})
.then(data => console.log(data))
.catch(error => console.error(error))
// => gives good data
Contents of the browser example for reference
import { gql, graphql } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloProvider } from 'react-apollo';
import App from './App';
const uri = 'https://qkv3mq9rp.lp.gql.zone/graphql'
const clientA = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
});
const clientB = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
});
const clientC = new ApolloClient({
link: new HttpLink({uri}),
cache: new InMemoryCache()
});
const query = gql`{
people {
id
name
}
}
`
clientA.query({query})
.then(data => document.write('A cache<br \><br \>' + JSON.stringify(data)))
.then(() => clientB.query({query, fetchPolicy: 'no-cache'}))
.then(data => document.write('<br \><br \>B no-cache<br \><br \>' + JSON.stringify(data)))
.then(() => clientC.query({query}))
.then(data => document.write('<br \><br \>C cache<br \><br \>' + JSON.stringify(data)))
.then(() => clientC.query({query, fetchPolicy: 'no-cache'}))
.then(data => document.write('<br \><br \>C no-cache<br \><br \>' + JSON.stringify(data)))
.catch(error => console.error(error))
Thanks for detailed report @razor-x. I have the same issue :)
@rschef I need this to work in a system which doesn't know about react and uses this client directly, so unfortunately that workaround won't be an option for me.
I poked around a little in QueryManager.ts but I couldn't find an obvious reason for this bug.
I'm hoping someone more familiar with the code will be able to prioritize and find this quickly. I would consider this a serious bug as I could rephrase the issue as apollo-client does not support the most basic GraphQL operation: running a query and returning the correct result (without the added complexity of caching).
It's this code:
https://github.com/apollographql/apollo-client/blob/6eec674f206cda11cf667a89022354b1476f5503/packages/apollo-client/src/core/QueryManager.ts#L1059-L1112
It was introduced in https://github.com/apollographql/apollo-client/pull/2934
Basically, if fetchMoreForQueryId
is not present, it tries to get the result from the cache even though it was never set (right above, if (fetchPolicy !== "no-cache")
).
The branch needs to be updated to use result.data
if no-cache
is used, I think:
if (fetchMoreForQueryId || fetchPolicy === "no-cache")
Not sure what implications that has.
I'm running into this right now because the cache actually seems to be causing performance issues in our use case and I wanted to try bypassing it; but I can't due to this bug :( If I can figure out how to develop this locally, I could try updating it.
@jbaxleyiii Should I try to update the branch as I mentioned to fix this issue? Do you see any issues with that approach?
Alright, I am debugging this issue actively.
I think my "fix" will work, however there's another problem.
In QueryManager, the queryListenerForObserver
will check the lastResult
of the ObservableQuery. When using anything other than no-cache
fetchPolicy, the above code will write to the data store. Then, right here:
If lastResult
ends up being null/undefined, the code assumes the data exists in the store and uses that. Hence, even with my fix, it doesn't work because queryListenerForObserver
will call observer.next()
with the null data before fetchRequest
calls complete and resolve
with the actual data from the network request.
I'm a little disappointed, because a simple test showcases this bug.
it('supports no-cache fetchPolicy query', () => {
const nonCachedQuery = gql`
query nonCachedQuery {
luke: people_one(id: 1) {
name
}
}
`;
const data1 = {
luke: {
name: 'Luke Skywalker',
},
};
const queryManager = mockQueryManager({
request: { query: nonCachedQuery },
result: { data: data1 },
});
return queryManager.query<any>({
query: nonCachedQuery,
fetchPolicy: 'no-cache',
}).then(result => {
expect(result.data['luke'].name).toBe('Luke Skywalker');
});
});
it('supports no-cache fetchPolicy watchQuery', () => {
const nonCachedQuery = gql`
query nonCachedQuery {
luke: people_one(id: 1) {
name
}
}
`;
const data1 = {
luke: {
name: 'Luke Skywalker',
},
};
const queryManager = mockQueryManager({
request: { query: nonCachedQuery },
result: { data: data1 },
});
return queryManager.watchQuery<any>({
query: nonCachedQuery,
fetchPolicy: 'no-cache',
}).result().then(result => {
console.log(result);
expect(result.data['luke'].name).toBe('Luke Skywalker');
});
});
Now I'm trying to figure out the best way to essentially get the data in a place queryListenerForObserver
can access it that is not the store--or figure out a way to call observer.next
some other way. I'm not well equipped to figure that out but I'll keep trying.
I have the tests passing, I just have it use setQuery
to set the newData
object manually if using no-cache
. I'll begin a PR and I will also test locally to see if it's addressing my issue...
I'm getting an eerily similar issue (same data shape, despite getting a valid gql response, default fetch policy), but only when building minified production assets for the apollo-client. That leads me to believe it's something in our code, but I'm not sure if any apollo libs look for NODE_ENV=production
.
Any updates on this?
This makes things pretty much unusable for me in some cases. I tried no-cache to actually get a proper loading indicator, as with the default fetch policy loading sometimes returns false even tho data is being loaded from remote. Now I get a proper loading indicator, but data is undefined (not null as others report) when loading finished. Changing things back to the default fetch-policy gives me the data, but again a misleading (false) loading state. 馃
This was fixed in release 2.3.8 by hwillson
I'm still having this issue (data is {}) on 2.4.2
I'm still having this issue with the latest version 2.4.5
"apollo-client": {
"version": "2.4.5",
"requires": {
"@types/async": "2.0.50",
"@types/zen-observable": "^0.8.0",
"apollo-cache": "1.1.20",
"apollo-link": "^1.0.0",
"apollo-link-dedup": "^1.0.0",
"apollo-utilities": "1.0.25",
"symbol-observable": "^1.0.2",
"zen-observable": "^0.8.0"
},
output is:
queryObj.context = this.getContext();
this.graphqlClient.authClient.query(
queryObj
).then(data => {
{
data: null
loading: false
networkStatus: 7
stale: true
}
but I could see the results in the network section in my browser and I could get non null data only If I add this to my apollo client:
,defaultOptions: {
query: {
fetchPolicy: "no-cache"
}
}
@kamranayub this issue still persists randomly.
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.
Please re-open this issue or let me know if I need to open a new one.
I have the same issue as @thanpolas, also happening erratically. Running version 2.4.7
of apollo-client
. If I console.log
the networkStatus
, I see that in the cases where it breaks it logs:
networkStatus = 1
networkStatus = 7
networkStatus = 7
instead of the expected:
networkStatus = 1
networkStatus = 7
Confirming that this is still an issue. We're encountering this when mocking network requests during E2E tests with Cypress.
After several trails I figured data
is always null whenever there are missing fields from the projected result. For instance, if we're supposed to query an user like: user { uuid email picture }
and for some reason the server does not return field picture
, then result will be:
{
data: null
loading: false
networkStatus: 7
stale: true
}
I've also noticed we'll get a Missing field picture
warning on the console.
In my particular case, my backend never serializes null
values. Since user's picture and many other fields are optional (nullable), we constantly face this problem.
Does anyone have an idea why those missing fields break the result causing this issue?
Seeing something that could be related to this but do not have the ability to reproduce ATM. We have a query that is inside of a component that is not being re-rendered (checked with consoles on render()) but the context provider for the query returns null for data.
Using partialRefretch: true
seems to help, but results in the data being null for a short period. While the data goes missing there is no graphql call in the console.
We encountered this issue and resolved via insight from @jdorleans's comment. When the response does not include a field you're expecting the entire data
object will be null.
I forgot to mentioned I created a new issue (#4267) to keep track of the problem I'm facing since it might differ from this one here.
Have same issue on [email protected], why this is closed?
The same problem with the Query component. It returns normal data once, then data = {} with the same query (nothing is changed just component is rerendered) when fetchPolicy is 'no-cache'
Ironically, this happens for me when my policy is "network-only". Switching to "no-cache" (what's the difference, by the way? both are making the network request anyways) fixes it.
@rivertam are you using the client and setting "network-only" or the Query component?
@audiolion Client in this case
@rivertam use Query if you can, its a bug in apollo client, see
@rivertam Same here. Using Apollo Client, switch to "no-cache" solve the problem
I'm actually experiencing this issue from multiple sides.
In some cases, the query works with "network-only" but does not work with "no-cache", whereas, in other queries, "network-only" does not work, but "no-cache" does.
I've tried to see whether my issue might be related to the missing fields @jdorleans mentioned, but that does not seem to be the case.
Oh, and data indeed does seem to be undefined
instead of null
, as @sreuter mentioned.
Somehow, reloading the whole page does seem to solve the issue & everything is fetched properly.
Why is this closed again? There's not even a workaround, save an actual fix for this.
I'm with the same problem that @jdorleans.
Apollo is a great library, but I'm facing a lot of bugs :(
Still having the same issues as @bozskejkaja
Neither no-cache or network-only are working for me when making @client side queries.
I still having the same issues with [email protected]
. It only works the first query, if you request the same data always is empty, if you change some fields it works only the first request.
no-cache
solved the problem for me on 2.5.1
Same issue.
All my problems were solved with errorPolicy: "all"
const client = new ApolloClient({
cache: new InMemoryCache(),
link: errorLink.concat(link),
resolvers: {},
defaultOptions: {
query: {
errorPolicy: "all"
}
}
});
After several trails I figured
data
is always null whenever there are missing fields from the projected result. For instance, if we're supposed to query an user like:user { uuid email picture }
and for some reason the server does not return fieldpicture
, then result will be:{ data: null loading: false networkStatus: 7 stale: true }
I've also noticed we'll get a
Missing field picture
warning on the console.In my particular case, my backend never serializes
null
values. Since user's picture and many other fields are optional (nullable), we constantly face this problem.Does anyone have an idea why those missing fields break the result causing this issue?
This insight from @jdorleans resolved the issue for me.
@jdorleans You have no idea how many hours you saved me. I was going in a rabbit hole trying to find the answer. Thank you
Most helpful comment
After several trails I figured
data
is always null whenever there are missing fields from the projected result. For instance, if we're supposed to query an user like:user { uuid email picture }
and for some reason the server does not return fieldpicture
, then result will be:I've also noticed we'll get a
Missing field picture
warning on the console.In my particular case, my backend never serializes
null
values. Since user's picture and many other fields are optional (nullable), we constantly face this problem.Does anyone have an idea why those missing fields break the result causing this issue?