Next.js14服务端组件中Apollo Client传递JWT令牌失败求助
问题:Next.js 14服务端组件中Apollo Client传递JWT令牌失效
在Next.js 14服务端组件中,通过Next Auth获取JWT令牌后,尝试通过Apollo Client的context传递令牌到查询请求,但带认证的请求不生效。无认证时查询可正常执行,现有代码如下:
Apollo配置代码
import { HttpLink, from } from "@apollo/client"; import { NextSSRInMemoryCache, NextSSRApolloClient, } from "@apollo/experimental-nextjs-app-support/ssr"; import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; import { onError } from "@apollo/client/link/error"; const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); if (networkError) console.log(`[Network error]: ${networkError}`); }); const httpLink = new HttpLink({ uri: process.env.NEXT_PUBLIC_WORDPRESS_API_URL, }); export const { getClient } = registerApolloClient(() => { return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: from([errorLink, httpLink]), }); });
页面组件代码
import { notFound } from "next/navigation"; import { headers } from "next/headers"; import { getClient } from "@utils/apolloclient"; import { getServerSession } from "next-auth/next"; import GET_DRAFT_POST from "@/lib/queries/wordpress/GET_DRAFT_POST"; import BlogPage from "@/components/blog/blogpage"; import authOptions from "@utils/auth"; export const dynamic = "force-dynamic"; export const metadata = { title: "Bingo Card Generator - Create Custom Printable Cards Online", description: "Discover our interactive bingo card generator. Create custom cards from templates or scratch. Print for live games or share for online fun!", }; export default async function Page({ params, searchParams }) { const postId = searchParams?.id; const postType = searchParams?.post_type; if (!postId || !postType) { notFound(); } const session = await getServerSession(authOptions); const token = session?.user?.jwtAuthToken; const { data } = await getClient().query({ query: GET_DRAFT_POST, variables: { id: postId, asPreview: false, }, context: { fetchOptions: { headers: { Authorization: "Bearer " + token + "" }, }, }, }); console.log(data); if (!data?.contentNode) { notFound(); } return ( <div className="page-content"> {postType === "post" && ( <BlogPage post={data.contentNode} newsletterSettings={data?.newsletterSettings} params={{ slug: data?.contentNode?.slug }} /> )} </div> ); }
解决方案
问题核心是静态初始化的HttpLink无法读取查询时传入的context参数,需要修改HttpLink配置,让它能动态获取每个请求的自定义headers。
1. 修改Apollo Client的HttpLink配置
更新HttpLink,添加自定义fetch函数,从请求context中读取并注入headers:
import { HttpLink, from } from "@apollo/client"; import { NextSSRInMemoryCache, NextSSRApolloClient, } from "@apollo/experimental-nextjs-app-support/ssr"; import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; import { onError } from "@apollo/client/link/error"; const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); if (networkError) console.log(`[Network error]: ${networkError}`); }); // 动态读取context中的headers const httpLink = new HttpLink({ uri: process.env.NEXT_PUBLIC_WORDPRESS_API_URL, fetch: async (uri, options) => { // 提取context里传入的自定义headers const customHeaders = options.context?.fetchOptions?.headers || {}; return fetch(uri, { ...options, headers: { ...options.headers, ...customHeaders, }, }); }, }); export const { getClient } = registerApolloClient(() => { return new NextSSRApolloClient({ cache: new NextSSRInMemoryCache(), link: from([errorLink, httpLink]), }); });
2. 优化页面组件的令牌传递逻辑
避免token为空时传递无效的Authorization头,只在token存在时添加context配置:
// 替换原查询代码 const queryOptions = { query: GET_DRAFT_POST, variables: { id: postId, asPreview: false, }, }; // 只有token存在时才添加认证头 if (token) { queryOptions.context = { fetchOptions: { headers: { Authorization: `Bearer ${token}` }, }, }; } const { data } = await getClient().query(queryOptions);
3. 额外验证步骤
- 在页面组件中添加
console.log(token),确认能获取到有效的JWT字符串。 - 检查后端接口是否正确解析
Authorization: Bearer <token>格式的请求头,确保令牌校验逻辑正常。
内容的提问来源于stack exchange,提问作者vipul
相关产品推荐
相关产品推荐

