You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:45:19