使用Apollo客户端在nextJs中传递授权头的最佳方式是什么?ReferenceError:未定义localStor



我正试图使用nextJs和apollo客户端从graphql服务器获取受保护的资源。我将授权令牌存储在客户端浏览器(localstorage(中,并尝试从apolloClient.Js文件中读取该令牌;但它抛出ReferenceError(ReferenceError:localStorage未定义(。这让我很快理解了服务器端试图从后端引用localStorage;但是由于它仅在客户端中可用而失败。我的问题是,解决这个问题的最佳方法是什么?我只是在我的项目中第一次使用apollo客户端。我花了10多个小时试图找出这个问题的解决办法。我在网上试过很多东西;没有幸运地得到解决方案。以下是我在apolloClient文件中使用的代码:

import { useMemo } from 'react'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
import { concatPagination } from '@apollo/client/utilities'
import { GQL_URL } from '../utils/api'
let apolloClient
const authToken = localStorage.getItem('authToken') || '';
function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: new HttpLink({
      uri: GQL_URL, // Server URL (must be absolute)
      credentials: 'include', // Additional fetch() options like `credentials` or `headers`
      headers: {
        Authorization: `JWT ${authToken}`
      }
    }),
    
    cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            allPosts: concatPagination(),
          },
        },
      },
    }),
  })
}
export function initializeApollo(initialState = null) {
  const _apolloClient = apolloClient ?? createApolloClient()
  // If your page has Next.js data fetching methods that use Apollo Client, the initial state
  // gets hydrated here
  if (initialState) {
    _apolloClient.cache.restore(initialState)
  }
  // For SSG and SSR always create a new Apollo Client
  if (typeof window === 'undefined') return _apolloClient
  // Create the Apollo Client once in the client
  if (!apolloClient) apolloClient = _apolloClient
  return _apolloClient
}
export function useApollo(initialState) {
  const store = useMemo(() => initializeApollo(initialState), [initialState])
  return store
}

只有当窗口对象不是"未定义"时,我才能通过访问本地存储来解决问题;因为它在服务器端是"未定义的"。这将很好地工作,因为我们不希望服务器访问本地存储。

import { useMemo } from 'react'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { GQL_URL } from '../utils/api'
let apolloClient
function createApolloClient() {
  // Declare variable to store authToken
  let token;
   
  const httpLink = createHttpLink({
    uri: GQL_URL,
    credentials: 'include',
  });
  const authLink = setContext((_, { headers }) => {
    // get the authentication token from local storage if it exists
    if (typeof window !== 'undefined') {
      token = localStorage.getItem('authToken');
    }
    // return the headers to the context so httpLink can read them
    return {
      headers: {
        ...headers,
        Authorization: token ? `JWT ${token}` : "",
      }
    }
  });
  const client = new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: authLink.concat(httpLink),
    cache: new InMemoryCache()
  });
  return client;
}

我可以看到这个问题已经解决了。但只是部分。目前,这对于进行授权的客户端查询是可以的,但如果有人试图在服务器端进行授权的查询,那么这将是一个问题,因为它无法访问本地存储。

所以修改这个:

//AUTH_TOKEN is the name you've set for your cookie
let apolloClient;
const httpLink = createHttpLink({
  uri: //Your URL,
});
const getAuthLink = (ctx) => {
  return setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: isSSR()
          ? ctx?.req?.cookies[AUTH_TOKEN] // server-side auth token
          : getPersistedAuthToken(), /* This is your auth token from 
          localstorage */
      },
    };
  });
};
function createApolloClient(ctx) {
  return new ApolloClient({
    ssrMode: typeof window === undefined,
    link: from([getAuthLink(ctx), httpLink]),
    cache: new InMemoryCache(),
  });
}
export function initializeApollo({ initialState = null, ctx = null }) {
  const _apolloClient = apolloClient ?? createApolloClient(ctx);
  if (initialState) {
    const existingCache = _apolloClient.extract();
    _apolloClient.cache.restore({ ...existingCache, ...initialState });
  }
  if (isSSR()) return _apolloClient;
  if (!apolloClient) apolloClient = _apolloClient;
  return _apolloClient;
}

getServerSide函数如下所示:

export async function getServerSideProps(ctx) {
  const { req } = ctx;
  if (req?.cookies[AUTH_TOKEN]) {
    const apolloClient = initializeApollo({ initialState: null, ctx });
    try {
      const { data } = await apolloClient.query({
        query: GET_USER_DETAILS,
      });
      // Handle what you want to do with this data / Just cache it
    } catch (error) {
      const gqlError = error.graphQLErrors[0];
      if (gqlError) {
        //Handle your error cases
      }
    }
  }
  return {
    props: {},
  };
}

这样,apollo客户端也可以用于在服务器端进行授权的调用。

最新更新