使用 Apollo 客户端在 nextJs 中传递授权标头的最佳方法? ReferenceError: localStorage 未定义
Posted
技术标签:
【中文标题】使用 Apollo 客户端在 nextJs 中传递授权标头的最佳方法? ReferenceError: localStorage 未定义【英文标题】:The best way to pass authorization header in nextJs using Apollo client? ReferenceError: localStorage is not defined 【发布时间】:2020-11-13 04:07:51 【问题描述】:我正在尝试使用 nextJs 和 apollo 客户端从我的 graphql 服务器获取受保护的资源。我将授权令牌存储在客户端浏览器(本地存储)中并尝试从 apolloClient.Js 文件中读取令牌;但它会引发 ReferenceError(ReferenceError: localStorage is not defined)。这让我很快明白服务器端试图从后端引用 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(
s-s-rMode: 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 s-s-r 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
【问题讨论】:
您是在服务器还是客户端收到错误? 错误来自服务器! 请同时发布您的服务器代码 我指的是next js的服务端;我的 graphql 服务器没有错误 您找到解决方案了吗?我无法添加授权标头,因为在服务器中创建的 Apollo Client 实例已传递给客户端,并且您无法访问服务器中的任何 cookie 或 localstorage。 【参考方案1】:我可以看到这个问题已经解决了。但只是部分。现在这对于进行授权的客户端查询很好,但是如果有人试图在服务器端进行授权查询,那么这将是一个问题,因为它无法访问本地存储。
所以修改这个:
//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: iss-s-r()
? ctx?.req?.cookies[AUTH_TOKEN] // server-side auth token
: getPersistedAuthToken(), /* This is your auth token from
localstorage */
,
;
);
;
function createApolloClient(ctx)
return new ApolloClient(
s-s-rMode: 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 (iss-s-r()) 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 客户端也可用于在服务器端进行授权调用。
【讨论】:
【参考方案2】:只有当窗口对象不是“未定义”时,我才能通过访问本地存储来解决问题;因为它将在服务器端“未定义”。这会很好用,因为我们不希望服务器访问本地存储。
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(
s-s-rMode: typeof window === 'undefined',
link: authLink.concat(httpLink),
cache: new InMemoryCache()
);
return client;
【讨论】:
以上是关于使用 Apollo 客户端在 nextJs 中传递授权标头的最佳方法? ReferenceError: localStorage 未定义的主要内容,如果未能解决你的问题,请参考以下文章
使用 NextJs 将 JWT 令牌传递到 Apollo 上下文
如何在客户端 NextJS 中使用 Apollo GraphQL 订阅?
如何从 NextJS 服务器为 Apollo 客户端补充水分
在 nextJS 上配置 apollo 客户端以进行订阅的问题