Query data and error are undefined
Intended outcome:
I'm using useQuery to query and render some data. The loading prop works as expected, the value is true and then changes to false. When loading is false, data and error are both undefined.
I checked the network tab and the data is being received (I see the data prop)

I also checked what's going on with the Apollo chrome extension and I can see the data

I also was able to verify the correct result from the BE using the fetch API

Actual outcome:
data and error props from useQuery are undefined.
How to reproduce the issue:
Here's the component that uses useQuery and fetch
const QUERY = gql`
query AccountStatus {
accountStatus {
__typename
missingCode
completed
reason
}
}
`
const MissingThings = () => {
const x = useQuery(QUERY)
const { loading, data, error } = x
console.log('--x', x)
useEffect(() => {
fetch(`${process.env.REACT_APP_GRAPHQL_URL}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization:
'Bearer <TOKEN>',
},
body: JSON.stringify({
variables: {},
query: `
query AccountStatus {
accountStatus {
__typename
missingCode
completed
reason
}
}
`,
}),
})
.then((result) => result.json())
.then((result) => console.log('--result', result))
.catch(console.error)
}, [])
if (loading) {
return null
}
if (error) {
console.log('--error', error)
return null
}
console.log('--data', data)
return <div>All good</div>
}
And this is the Apollo Client
const ApolloClientProvider = ({ children }: any) => {
const { auth, account } = useGlobalProvider()
const headers =
auth && account ? { Authorization: `Bearer ${auth?.token}` } : {}
console.log('--headers', headers)
console.log('--auth', auth)
const wsLink = new WebSocketLink({
uri: process.env.REACT_APP_GRAPHQL_WS_URL as string,
options: {
reconnect: true,
connectionParams: () => ({
headers,
}),
},
})
const httpLink = new HttpLink({ uri: process.env.REACT_APP_GRAPHQL_URL })
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext({
headers:
auth && account ? { Authorization: `Bearer ${auth?.token}` } : {},
})
return forward(operation)
})
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink,
)
const logoutLink = onError((error) => {
console.log('APOLLO ERROR!', error)
if (
error.networkError?.message.includes('JWTExpired') ||
error.graphQLErrors?.some(
({ extensions, message }) =>
extensions?.code === 'invalid-jwt' || message.includes('JWTExpired'),
)
) {
navigate('/logout')
}
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([logoutLink, authMiddleware, splitLink]),
})
return <ApolloProvider client={client}>{children}</ApolloProvider>
}
Versions System: OS: macOS 11.2.3 Binaries: Node: 14.8.0 - ~/n/bin/node npm: 6.14.7 - ~/n/bin/npm Browsers: Chrome: 90.0.4430.72 Safari: 14.0.3 npmPackages: @apollo/client: ^3.3.15 => 3.3.15
I continued testing and I found out that not using the cache works:
const x = useQuery(QUERY, {
fetchPolicy: 'network-only',
})
Any idea of how to debug the cache? I see that the data is cached but Apollo is having a hard time retrieving it (that's my conclusion)

I'm having the same bug of data being undefined but when backgrounding my app and foregrounding it. Verified via flipper that the data was actually coming in though. Setting fetchPolicy: network-only didnt solve it for my case.
"@apollo/client": "^3.3.15",
"react-native": "0.63.2",
"apollo3-cache-persist": "^0.9.1",
I’ve also run into this situation when implementing defer for Apollo Client. It seems like if the server responds with data which doesn’t match the query, in that it’s missing fields, the result provided by the cache has data and error set to undefined and loading set to false, which should probably never happen. Here’s a quick reproduction.
/*** SCHEMA ***/
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLID,
GraphQLString,
GraphQLList,
} from 'graphql';
const PersonType = new GraphQLObjectType({
name: 'Person',
fields: {
id: {type: GraphQLID},
name: {type: GraphQLString},
},
});
const peopleData = [
{id: 1, name: 'John Smith'},
{id: 2, name: 'Sara Smith'},
{id: 3, name: 'Budd Deey'},
];
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: {
people: {
type: new GraphQLList(PersonType),
resolve: () => peopleData,
},
},
});
const schema = new GraphQLSchema({query: QueryType});
/*** LINK ***/
import {graphql, print} from "graphql";
import {ApolloLink, Observable} from "@apollo/client";
function delay(wait) {
return new Promise(resolve => setTimeout(resolve, wait));
}
const link = new ApolloLink(operation => {
return new Observable(async observer => {
const {query, operationName, variables} = operation;
await delay(300);
try {
const result = await graphql(
schema,
print(query),
null,
null,
variables,
operationName,
);
// Emulate producing a result which is missing a field from the server.
delete result.data.people[2].name;
observer.next(result);
observer.complete();
} catch (err) {
observer.error(err);
}
});
});
/*** APP ***/
import React from "react";
import {render} from "react-dom";
import {
ApolloClient,
ApolloProvider,
InMemoryCache,
gql,
useQuery,
} from "@apollo/client";
import "./index.css";
const ALL_PEOPLE = gql`
query AllPeople {
people {
id
name
}
}
`;
function App() {
const {loading, data, error} = useQuery(ALL_PEOPLE);
if (loading) {
return <div>Loading...</div>;
} else if (error) {
return <div>Error: {error.toString()}</div>;
} else if (!data) {
return <div>What the heck</div>;
}
return (
<ul>
{data.people.map(person => (
<li key={person.id}>{person.name}</li>
))}
</ul>
);
}
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root"),
);
@benjamn Do you know the original behavior for when a server responds with less data than the query expects? My sense is that this is a regression of some kind.
I’m discussing this with the team, and one thing y’all should try is adding returnPartialData to the query options. Still investigating as to what the expected behavior should be.
Just noticed this bug today. Can also confirm that setting a fetchPolicy that does not read from cache resolves the issue. On latest version, @apollo/[email protected].
What's really bizarre is that I've only noticed this error recently. Yet, when I downgrade to v3.0.0, the error persists.
Do you know the original behavior for when a server responds with less data than the query expects? My sense is that this is a regression of some kind.
@brainkim I'm working on a new project with my company that involves a very simple query that reproduces this error consistently. I can say safely that the fields my mock server is returning are the exact same as the fields my client is expecting. Sadly, the code is private so I can't give you the repo.
Basically I'm reproducing this error when I run 2 different queries that have the same cache key. I have a
useQuery(GET_USERS)
called earlier, which returns an array of all users including one with ID asdf, and a
useQuery(SELECT_USER, { variables: { id: 'asdf' } })
called later. The second useQuery fails silently and eventually resolves to { loading: false, error: false, data: undefined }.
I was able to reproduce this issue on a separate repository. See below @brainkim
https://github.com/diracs-delta/apollo-client-8063
@diracs-delta This is a beautiful example. I’m dropping everything to investigate.
@diracs-delta Ah I get it. It’s the same problem as I think everyone in this issue is having. In the mock graphql server you need to put the user object under the key user, or it doesn’t actually match the GraphQL query you sent to the server.
const handlers = [
graphql.query('getUsers', (_, res, ctx) => {
return res(ctx.data({ users: userStore }))
}),
graphql.query('getSpecificUser', (req, res, ctx) => {
const user = userStore.find(user => user.id === req.variables.id);
if (!user) {
return res(ctx.errors([{ message: `Cannot find user ${user}` }]));
}
return res(ctx.data({user}));
}),
];
@brainkim I believe you're right. The useQuery hook is silently failing when the response doesn't match the query exactly, but the response has a corresponding cache entry.
It would be nice to produce some kind of console warning or emit an error when this happens to prevent future confusion. I can take a further look at this later tonight.
Hello, I'm experiencing this same issue, however I am not mocking anything.
As @brainkim suggested above, adding returnPartialData allows my useQuery hook to work again and properly return data. However, I'm not keen on adding that to all my queries with caching involved?
Is there somewhere in the cache setup where this can be addressed? Or what long term solution could I implement that doesn't involve adding the returnPartialData option to all my useQuery hooks?
@jasongaare
If you are both the provider and consumer of the GraphQL API, this issue is occurring because the field structure of the query and the response do not match, which is usually due to the backend omitting a null field entirely instead of returning null for a null field. Example:
// => request
query {
id
name
}
// <= response
data: {
id: 'asdf'
// should be name: null
}
Otherwise, if you are only the consumer of the GraphQL API, you're unfortunately out of other options until this issue is resolved.
Where is the requirement to return "null" instead of "undefined" documented? Is that a graphql spec kind of thing? I hit this hard when I tried to change my Maybe type to T | undefined instead of T | null in graphql code-gen. Is that just not going to work on the client b/c the spec requires "null"?
@macrael Although I truthfully haven't read the spec top-to-bottom, I believe that to be the case. I think the provider of a GraphQL API should always explicitly return null for a field rather than omitting it in the response, but someone please correct me if I'm wrong.
The reason codegen defines Maybe<T> = T | undefined | null by default is because codegen is intended to be used primarily for client-side development, in which case the type of Maybe is very convenient. This way, in queries with optional parameters, you can just omit the optional parameters instead of explicitly specifying each to be null. This omission is definitely in the GraphQL spec for request variables, but not for response data.
Since keeping the default definition of Maybe<T> is really convenient when you are using query variables, I suggest just leaving the value of Maybe<T> untouched when doing client-side work. But if you're doing backend work, you should define Maybe<T> = null | T instead.
I've also encountered this issue, and setting fetchPolicy: network-only is not fixing it.
The oddest thing is that when I'm on a page where an object is queried for some fields, and then I open a component where the same object is queried for different fields, it is WORKING.
However, when I'm on a page that doesn't contain any graphql query, and I open the same component that queries the object, the issue happens. In the network tab in dev tools the API actually returned the right data, but in the code where I did await client.query I got undefined.
I've tried to add ID to each of the object and subobjects and pass in fetchPolicy: no-cache or fetchPolicy: network-only, but nothing works.
I'm not doing watchQuery so returnPartialData is not an option.
The component is rendered by ReactDOM.render. I've checked appollo dev tool, and the data from this component is not appearing in the dev tool no matter the data shows correctly in UI or not (for both cases mentioned above). In the meanwhile, another graphql call in the page shows in the cache.
This should be resolved in @apollo/client@latest - let us know if not.
Trying to install the package @apollo/client@latest.
Yarn version:
1.22.10
Node version:
14.17.4
Platform:
darwin x64
Trace:
Invariant Violation: expected manifest
at invariant (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:2314:15)
at PackageResolver.getStrictResolvedPattern (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:65361:5)
at add (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:94632:33)
at add (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:94673:9)
at add (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:94673:9)
at PackageHoister.prepass (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:94706:7)
at PackageHoister.seed (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:94098:10)
at PackageLinker.getFlatHoistedTree (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:48740:13)
at PackageLinker.<anonymous> (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:48755:27)
at Generator.next (<anonymous>)
I have the solution just to get the data out . My solution is this one. (it is temporary) :)) I wait for the bug to be solved
const {loading, error, data} = useQuery(queries.GET_CATEGORIES); //at this moment the data is undefined //using the spread operator I create a new object with the data content const prod = {...data}; console.log(prod);
I can confirm that this is still happening at 3.5.10. If one of the fields requested is null (because of an error), both errors and data fields are undefined from useQuery returned object.
Note: I can see in the network tab that request has 200 response code, has data and errors fields set as expected in the response.
I'm seeing the same thing (@apollo/client 3.5. 10):
useQuery returns undefined for error and data, though I can see that loading is true at some point.
We both consume and provide the GraphQL API, so I have full ability to debug on both ends. The network response is exactly as we'd expect. Notably, there are no errors in the network response; the shape of the response is exactly as we'd expected, with the queried object nested under data.
For now, we've downgraded back to 3.2.5 to resolve this.
I had the same problem, the base url was wrong export const client = new ApolloClient({ uri: process.env.NEXT_PUBLIC_BASE_URL + "/api/graphql", cache, });
I just ran into this problem, as well. Fixed it by downgrading to 3.2.5.
I just ran into this problem too... data and error undefined
Same problem 🥲
Same issue happening at 3.6.1.
3.5.10 works for me 🔥
In addition, my snapshot tests using "@apollo/client": "3.5.10" and "@testing-library/react": "^13.1.1" look like:
it('Renders correctly for a successful call', async () => {
const { container } = render(
<MockedProvider
defaultOptions={{ watchQuery: { fetchPolicy: 'network-only' } }}
mocks={[mocksNeeded]}
addTypename={false}
>
<AllTheChildren/>
</MockedProvider>,
)
await waitFor(() => new Promise((res) => setTimeout(res, 500)))
expect(container).toMatchSnapshot()
})
same problem here. fixed by downgrading to 3.2.5
downgrading to 3.2.5 works for me
Tried a few suggestions here, I can confirm that both 3.2.5 and 3.5.10 are working for me and decided to stay with 3.5.10.
I'm really curious also as to why no errors are being reported and we end up with a silent fail, this has been very time-consuming to debug.
Maybe this suggests a regression, let us know if there is anything else we can do to test or provide further debug info.
3.5.10 working for me. No joy with 3.6+