Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why is observer.error not handled in getClient or useSuspenseQuery? #259

Open
Algrus8 opened this issue Mar 27, 2024 · 3 comments
Open

Why is observer.error not handled in getClient or useSuspenseQuery? #259

Algrus8 opened this issue Mar 27, 2024 · 3 comments

Comments

@Algrus8
Copy link

Algrus8 commented Mar 27, 2024

Hello!
I have this code:

import { onError } from '@apollo/client/link/error';
import type { FetchResult } from '@apollo/client/link/core/types';
import {
  type ApolloClient,
  type NormalizedCacheObject,
  Observable,
} from '@apollo/client';
import { type NextSSRApolloClient } from '@apollo/experimental-nextjs-app-support/ssr';
import {
  RefreshTokensDocument,
  type RefreshTokensMutation,
} from './refreshTokens.generated';

export const createAuthErrorLink = (
  getClient:
    | (() => ApolloClient<unknown>)
    | (() => NextSSRApolloClient<NormalizedCacheObject>)
) =>
  onError(({ graphQLErrors, networkError, operation, forward }) => {
    const isAuthError = graphQLErrors?.find(
      (error) => error.extensions.code === 'UNAUTHENTICATED'
    );

    if (isAuthError) {
      // Ignore 401 error for a refresh request.
      if (operation.operationName === 'RefreshTokens') return;
      const observable = new Observable<FetchResult>((observer) => {
        const refreshTokens = async () => {
          try {
            const { data } = await getClient().mutate<RefreshTokensMutation>({
              mutation: RefreshTokensDocument,
            });

            if (!data?.refreshTokens) {
              throw new Error('Refresh Tokens Error');
            }

            const { accessToken, refreshToken } = data.refreshTokens;

            const cookie = [
              `accessToken=${accessToken};`,
              `refreshToken=${refreshToken};`,
            ];

            const oldHeaders = operation.getContext().headers as object;

            operation.setContext({
              headers: {
                ...oldHeaders,
                cookie,
              },
            });

            forward(operation).subscribe(observer);
          } catch (error) {
            observer.error(error);
          }
        };
        void refreshTokens();
      });

      return observable;
    }

    if (networkError) console.error(`[Network error]: ${networkError}`);
  });

This is my getClient definition:

import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
import {
  ApolloLink,
  HttpLink,
  from,
  InMemoryCache,
  ApolloClient,
} from '@apollo/client';
import { cookies } from 'next/headers';
import { createAuthErrorLink } from './create-auth-error-link';

const cookieLink = new ApolloLink((operation, forward) => {
  const cookieHeader = cookies();
  const oldHeaders = operation.getContext().headers as object;

  operation.setContext({
    headers: {
      ...oldHeaders,
      cookie: cookieHeader,
    },
  });

  return forward(operation);
});

export const { getClient } = registerApolloClient(() => {
  const httpLink = new HttpLink({
    uri: 'http://127.0.0.1:3000/graphql',
    credentials: 'include',
  });
  const authErrorLink = createAuthErrorLink(getClient);

  return new ApolloClient({
    cache: new InMemoryCache(),
    link: from([cookieLink, authErrorLink, httpLink]),
  });
});

And when I use useQuery from '@apollo/client', everything works correctly. However, when I use getClient or useSuspenseQuery, for example:

const { data, error } = await getClient().query<GetCurrentUserQuery>({
  query: GetCurrentUserDocument,
});

I get an unhandled error in Next.js when calling observer.error(error);, and I can't retrieve the error just from the getClient result. How can I fix that?

@Algrus8 Algrus8 changed the title Why observer.error not handling in getClient or useSuspenseQuery? Why is observer.error not handled in getClient or useSuspenseQuery? Mar 28, 2024
@phryneas
Copy link
Member

phryneas commented Apr 2, 2024

In the useSuspenseQuery case, you should use an error boundary - and I believe in the await getClient().query case you can either use a try..catch block, or also an error boundary.

@Algrus8
Copy link
Author

Algrus8 commented Apr 2, 2024

Yes, I can handle this error with a try-catch, but can I do something to get it just from this error const { data, error } = await getClient().query?

@phryneas
Copy link
Member

phryneas commented Apr 2, 2024

You could set the errorPolicy in your query call options - the default is none, which will throw, but you could also set errorPolicy: "all" which would make it accessible the way you want to here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants