apollo-client: Errors lost on cached results

I’m using apollo and react to call a query that returns mixed results: error(s) and some data. In order to handle the errors, but still be able to use the information given, I’m using the errorPolicy: "all" option in the query.

<Query query={query} variables={variables as any} errorPolicy='all'>
    {({loading, error, data}) => { ... }}
</Query>

The first time I mount the component data is populated with the partial informations and error with the errors returned by the query. If I change the route (unmounting the component) and then return on it, it shows the cached partial data but no errors, so I’m not able to handle errors anymore and detect that these are partial informations.

Intended outcome: The component shows me the original errors along with the cached data.

Actual outcome: The props error is undefined, the partial data are passed as if the query didn’t return any error.

How to reproduce the issue: Create a query that returns both data and error.

Version

"react-apollo": "2.5.2", "apollo-client": "^2.5.1",

Reference to docs:

Screen Shot 2019-03-27 at 8 43 37 AM

From the docs ^^^^^

https://www.apollographql.com/docs/react/features/error-handling

If this issue looks familiar thats because i stole part of the description from here. An issue that was previously opened and then closed.

Im not sure why that issue was closed as a solution to the original issue was never found.

Note: I am NOT using SSR, i’m simply using the in meme cache.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 11
  • Comments: 25 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Can we expect this to be fixed by the Apollo team? Are there any workarounds for hook-based queries?

Ping @hwillson any updates here?

If I could at least get a confirmation that this is in-fact a bug and not something I’m doing wrong that would be greatly appreciated…

This was marked as hasProduction two months ago and nothing has been said? I want to assume i’m doing something wrong seeing as this seems like a large overlooked issue but I don’t think I am.

@hwillson I also have a reproduction for this case: https://codesandbox.io/s/qqqzp95vq9 , which I originally posted in this thread https://github.com/apollographql/apollo-client/pull/4237

@hwillson Ok ive got two sandboxes for ya!

https://codesandbox.io/s/w7qy5m1pw7 << UI using apollo client https://codesandbox.io/s/5wlv3jlywn << Server using apollo server

A couple things to note:

  1. The following parameters seem to have NO affect on the policies… you MUST put the param on the <Query> component.
defaultOptions: {
    watchQuery: {
      errorPolicy: "all"
    },
    query: {
      errorPolicy: "all"
    },
    mutate: {
      errorPolicy: "all"
    }
  }
  1. Putting errorPolicy="all" on the query makes it so the cacheing takes place ( the request is made every time other wise (i find this to be incorrect).

I ran into this issue as well. I’m lazy-loading many components on a page, each of which uses the same query hook, so the problem occurs for me if one of the components loads after another component already received the data from the hook. For those who are having this issue after lazy-loading entire pages, this solution may not be much of an improvement over just setting the default fetch policy to cache-and-network.

I attempted to follow @dannycochran’s example above, but felt it would be too complex and error-prone to create my own error cache, especially with our error policy set to “all”.

I ended up solving the problem by temporarily switching our fetch policy for usages of the hook that I know will reproduce the issue. If the lazy-loaded component mounts and never receives loading: true from the hook, I switch the hook to use fetchPolicy: "cache-and-network" for that 1 request. That immediately displays any cached data, but still retriggers the network request so that it passes along the error. All subsequent requests use fetchPolicy: "cache-first".

This adds +1 query whenever another component (or set of components) mounts that uses a previously cached query, but still saves some network requests over setting our default fetch policy to cache-and-network.

import { useRef } from 'react';
import { useQuery as useQueryOriginal } from '@apollo/client';

export const useQuery: typeof useQueryOriginal = (query, options) => {
  const didEnterLoadingStateRef = useRef(false);

  // First, run the query with the default fetch policy.
  const queryResultWithCache = useQueryOriginal(query, options);

  if (queryResultWithCache.loading) {
    didEnterLoadingStateRef.current = true;
  }

  // If the query is loading now, or if it loaded before, then we should skip
  // the cache-and-network query.
  const shouldSkipCacheAndNetworkQuery = didEnterLoadingStateRef.current;

  const queryResultWithCacheAndNetwork = useQueryOriginal(query, {
    ...options,
    fetchPolicy: 'cache-and-network',
    skip: options?.skip || shouldSkipCacheAndNetworkQuery,
  });

  return shouldSkipCacheAndNetworkQuery
    ? queryResultWithCache
    : queryResultWithCacheAndNetwork;
};

I’m also using GraphQL Code Generator to generate hooks for my queries, so instead of using the hook directly, I set apolloReactHooksImportFrom in the codegen config, so it uses this useQuery hook instead of the one it would normally import from @apollo/client.

If this matches your use case, then you’ll also need to add the following line to the above file so that it can access everything else in @apollo/client:

export * from '@apollo/client';

Hope this helps someone! And really hoping for a solution from the Apollo team so I can remove this workaround 😄

@tobobo we investigated doing the same, if only to benefit from typed/schema’d errors, but ultimately came up short due to having to reimplement graphQL’s field-level error logic from scratch, specific complexities with our use case, and the fact that errors may still be inevitable.

@jpvajda yes, still an issue, basically if you want to use apollo-client with SSR you can get apollo to prefetch the content of a page operation, store it into the cache and on hydration receive the cached contents.

however, in the scenario that a operation fails, you now lose that information because the apollo cache doesn’t store error responses into its cache, so if an error happened, you have no idea because the error is dropped by apollo, and the only way to safely deal with is by running the operation, checking if it had an error and build some mechinism outside of apollo to determine if the operation had an error before checking the contents of the cache.

note: I’m using “operation” here as a general term to mean either “query” or “mutation” as a graphql client.

I think this workaround works, at least with the default error policy. I think with some error policies onComplete would fire, so you’d reset your error when it could still exist. You could probably fix this to work with any error policy, you just would need to check what type it is.

import { OperationVariables } from 'apollo-client';
import { useQuery, QueryHookOptions } from '@apollo/react-hooks';

export const useWrapperQuery = <TData = any, TVariables = OperationVariables>(
  graphQlQuery: DocumentNode,
  options?: QueryHookOptions<TData, TVariables>
): QueryResult<TData> => {
  const [cachedError, setCachedError] = useState<ApolloError | undefined>();
  const queryResults = useQuery(graphQlQuery, {
    ...options,
    onCompleted: data => {
      options?.onCompleted?.(data);
      // here you might need to check your options.errorPolicy
      setCachedError(undefined);
    },
    onError: error => {
      options?.onError?.(error);
      setCachedError(error);
    },
  });
  return {
    ...queryResults,
    error: cachedError,
  };
};

I think this is a critical issue, without this cached error, there is no way to do error handling correctly. Error can only be showed once and once you navigate way and come back, the error is gone.

any updates?

FYI I’ve recreated @joepuzzo sandbox from above, with latest apollo versions, together with @apollo/react-hooks, using useQuery, the problem remains.

https://codesandbox.io/s/apolloerrorissue1withhooks-moee3 once you navigate to the “dog” path, second time, error disappears.

@hwillson , any news on caching errors?