urql-graphql / urql

The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

Home Page:https://urql.dev/goto/docs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Errors are returned before being able to redirect

IGassmann opened this issue · comments

Describe the bug

I have an app that uses Clerk and the authExchange for authentication.

'use client';

import { useMemo } from 'react';
import { useAuth } from '@clerk/nextjs';
import { authExchange } from '@urql/exchange-auth';
import { Provider, cacheExchange, createClient, fetchExchange, mapExchange } from 'urql';

export default function URQLProvider({ children }: { children: React.ReactNode }) {
  const { getToken, isSignedIn, signOut } = useAuth();

  const urqlClient = useMemo(() => {
    return createClient({
      url: '/api',
      exchanges: [
        cacheExchange,
        mapExchange({
          onError(error) {
            const isAuthError = error.response.status === 401;
            if (isAuthError) {
              signOut(); // This internally performs a redirect to the sign-in page with Next.js' router.push('/sign-in') function.
              // window.location.href = '/sign-in';  also doesn't work
              // router.push('/sign-in');            neither does this
            }
          },
        }),
        authExchange(async (utils) => {
          let sessionToken = await getToken();
          return {
            addAuthToOperation: (operation) => {
              if (!sessionToken) return operation;
              return utils.appendHeaders(operation, {
                Authorization: `Bearer ${sessionToken}`,
              });
            },
            didAuthError: (error) => error.response.status === 401,
            refreshAuth: async () => {
              sessionToken = await getToken({ skipCache: true });
            },
          };
        }),
        fetchExchange,
      ],
    });
  }, [getToken, isSignedIn]);

  return <Provider value={urqlClient}>{children}</Provider>;
}

Whenever a user signs out, all useQuery hooks on the page return an Unauthorized error for a brief moment before being redirected to the sign-in page.

The user needs to be redirected before seeing any error caused by the sign-out.

Reproduction

https://github.com/IGassmann/urql-clerk-signout

Urql version

urql v4.0.6

Validations

  • I can confirm that this is a bug report, and not a feature request, RFC, question, or discussion, for which GitHub Discussions should be used
  • Read the docs.
  • Follow our Code of Conduct

I mean, that's expected behavior as signing out and re-initializing the client forces us to re-evaluate the queries. You can however add pause: ifNoToken to the useQuery so they don't run when they are expected to return unauthorized due to i.e. a missing token. or not render anything if there is no auth-token which I think is something you most likely don't want

We still wanted to render children to perform operations without authentication in certain cases.

We solved the issue by adding a retryExchange after authExchange.