The best way to pass authorization header in nextJs using Apollo client? ReferenceError: localStorage is not defined
The best way to pass authorization header in nextJs using Apollo client? ReferenceError: localStorage is not defined
我正在尝试使用 nextJs 和 apollo 客户端从我的 graphql 服务器获取受保护的资源。我将授权令牌存储在客户端浏览器(本地存储)中并尝试从 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
}
只有当 window 对象不是 'undefined' 时,我才能够通过访问本地存储来解决问题;因为它将在服务器端 'undefined' 。这会很好用,因为我们不希望服务器访问本地存储。
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;
}
我看到这个问题已经解决了。但只是部分。现在这对于进行授权的客户端查询很好,但是如果有人试图在服务器端进行 authorized 查询,那么这将是一个问题,因为它没有访问本地存储。
所以修改这个:
//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 客户端也可用于在服务器端进行 授权 调用。
我正在尝试使用 nextJs 和 apollo 客户端从我的 graphql 服务器获取受保护的资源。我将授权令牌存储在客户端浏览器(本地存储)中并尝试从 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
}
只有当 window 对象不是 'undefined' 时,我才能够通过访问本地存储来解决问题;因为它将在服务器端 'undefined' 。这会很好用,因为我们不希望服务器访问本地存储。
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;
}
我看到这个问题已经解决了。但只是部分。现在这对于进行授权的客户端查询很好,但是如果有人试图在服务器端进行 authorized 查询,那么这将是一个问题,因为它没有访问本地存储。
所以修改这个:
//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 客户端也可用于在服务器端进行 授权 调用。