Apollo Client empty context (sometimes?)

Viewed 217

So I've run into this weird issue i cannot resolve. My client code was working fine until today. Without any updates to the packages or to the code related to apollo I've found out my context is not being handled properly. Basically I've split links so I can use 2 separate backend URL's but for some reason Context is empty. The very weird thing about this is that simple save on that file resolves this issue (on live server) for one fetch until I refresh. I've also noticed some of my DOM elements are not updating by useState hook as they should and same thing resolves that, just saving the file. I have no idea what can cause that issue so I'm hoping you can help me.

This is my apollo client code

import {
  ApolloClient,
  ApolloLink,
  createHttpLink,
  InMemoryCache,
} from "@apollo/client";
import { SITE_BACKEND_URL } from "../utility/globals";
import { TokenRefreshLink } from "apollo-link-token-refresh";
import { store } from "../state/store";
import { logOut, refreshJWT } from "../state/authSlice";

const customFetch = async (uri, options) => {
  return fetch(uri, {
    ...options,
    headers: {
      ...options.headers,
      Authorization: store.getState().auth.JWTToken
        ? `Bearer ${store.getState().auth.JWTToken}`
        : ``,
    },
  });
};

const httpLink = createHttpLink({
  uri: `${SITE_BACKEND_URL}/graphql`,
  fetch: customFetch,
});
const systemHttpLink = createHttpLink({
  uri: `${SITE_BACKEND_URL}/graphql/system`,
  fetch: customFetch,
});

export const cache = new InMemoryCache();

const client = new ApolloClient({
  connectToDevTools: true,
  link: ApolloLink.from([
    new TokenRefreshLink({
      isTokenValidOrUndefined: () => {
        // Check if JWT token is unindetified
        if (!store.getState().auth.JWTToken) {
          return true;
        }
        if (store.getState().auth.expire - new Date().getTime() >= 0) {
          return true;
        }
      },
      fetchAccessToken: async () => {
        if (!store.getState().auth.refreshToken) {
          return null;
        }
        const data = await fetch(`${SITE_BACKEND_URL}/graphql/system`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            query: ` mutation RefreshToken($token: String) {
              auth_refresh(refresh_token: $token) {
                access_token
                expires
                refresh_token
              }
            }`,
            variables: {
              token: store.getState().auth.refreshToken,
            },
          }),
        });
        return data.json();
      },
      handleResponse: (operation, accessTokenField) => async (response) => {
        if (!response.data?.auth_refresh) {
          store.dispatch(logOut());
          return { newToken: null };
        }
        store.dispatch(refreshJWT(response.data?.auth_refresh));

        return { newToken: response.data?.refreshUserToken?.token };
      },
    }),
    ApolloLink.split(
      (operation) => {
        console.log("triggered", operation.getContext());
        return operation.getContext().clientName === "system";
      },
      systemHttpLink,
      httpLink
    ),
  ]),
  credentials: "include",
  cache: cache,
});

export default client;

Ive put there console log so i can see the operation object and this is what i get

{forceFetch: false, cache: InMemoryCache, clientAwareness: {…}, getCacheKey: ƒ}

And what I get after just saving the client.js file:

{clientName: 'system', forceFetch: false, cache: InMemoryCache, clientAwareness: {…}, getCacheKey: ƒ}

Here are my dependencies maybe you can find something here:

  "dependencies": {
    "@apollo/client": "^3.5.8",
    "@reduxjs/toolkit": "^1.7.1",
    "antd": "^4.18.5",
    "apollo-link-token-refresh": "^0.3.3",
    "axios": "^0.25.0",
    "babel-plugin-import": "^1.13.3",
    "embla-carousel-react": "^6.1.1",
    "graphql": "^15.8.0",
    "js-cookie": "^3.0.1",
    "less": "^4.1.2",
    "next": "^12.0.8",
    "next-with-less": "^2.0.4",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-multiline-clamp": "^2.0.0",
    "react-redux": "^7.2.6"
  },
  "devDependencies": {
    "eslint": "8.7.0",
    "eslint-config-next": "12.0.8"
  }

The last package I installed is patch-package today but I removed it to see if it resolves the issue

1 Answers

To anyone who comes across this issue, I was facing a similar issue, and the problem was that I had another useMutation hook in my react component with a refetchQueries that was doing the same query that I was trying to do manually via a refetch, but was missing the context parameter. Below is an example and how I fixed it:

export default function MoviesList() {
  const [addMovie] = useMutation(CREATE_MOVIE, {
    context: {endpoint: "movies"},
    // This `refetchQueries` parameter was missing `context`, removing
    // this fixed the issue
    refetchQueries: [
      {
        query: GET_MOVIES,
        fetchPolicy: "network-only",
      },
    ],
  });

  const {
    refetch: fetchMovies,
  } = useQuery<{playbooks: Playbook[]}>(GET_MOVIES, {
    context: {endpoint: "movies"},
    skip: true,
    fetchPolicy: "network-only",
  });

  async function handleAddMovie() {
    await addMovie({title: "Hello World"});
    await fetchMovies();
  }

  return (<Movies>...</Movies>);
}
Related