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

Node.js环境下Apollo Client请求头不生效问题排查

问题根源分析

你的Apollo Client请求头未生效的核心原因是客户端配置的authLink覆盖了你在服务端调用时传入的自定义headers,具体细节:

  1. authLink中的setContext会在每次请求时执行,它会尝试从auth.getSession()获取token并设置Authorization头。但在服务端环境中,auth.getSession()大概率无法获取到浏览器端的用户session(因为服务端没有浏览器的存储/上下文),导致拿到的id_token是undefined,最终生成无效的Authorization: Bearer undefined头,直接覆盖了你传入的有效令牌。

  2. 即使你在mutate时传入了context.headers,authLink的逻辑是...headers, Authorization: xxx——这里的Authorization会强制覆盖传入值,不管你传的是用户令牌还是管理员令牌。

解决方案

方案1:修改authLink,优先保留传入的自定义headers

调整authLink的逻辑,当请求已经携带认证头时,不再覆盖它,仅在没有认证头时才从session获取token:

const authLink = setContext(async (_, { headers }) => {
  // 优先使用传入的认证头,避免覆盖服务端自定义配置
  if (headers?.Authorization || headers?.["x-hasura-admin-secret"]) {
    return { headers };
  }

  // 客户端环境下正常从session获取token
  try {
    const { id_token } = await auth.getSession();
    return {
      headers: {
        ...headers,
        Authorization: `Bearer ${id_token}`,
      },
    };
  } catch (err) {
    // 服务端环境获取session失败时,返回原headers
    return { headers };
  }
});

方案2:为服务端单独创建Apollo Client实例

服务端的认证逻辑和客户端完全不同,没必要共用同一个实例。直接创建一个不带authLink的专用客户端,避免客户端逻辑干扰:

// 服务端专用Apollo Client
export const serverApolloClient = new ApolloClient({
  link: new HttpLink({
    uri: process.env.API_URL_HTTP,
  }),
  cache: new InMemoryCache(),
});

然后服务端调用时使用这个实例:

await serverApolloClient
  .mutate({
    mutation: gql`
      mutation InsertSubscriptionId(
        $id: uuid!
        $stripe_subscription_id: String!
      ) {
        update_user_by_pk(
          pk_columns: { id: $id }
          _set: { stripe_subscription_id: $stripe_subscription_id }
        ) {
          id
        }
      }
    `,
    variables: {
      id: session.metadata.hasura_user_id,
      stripe_subscription_id: session.subscription,
    },
    context: {
      headers: {
        // 可以直接传管理员密钥或用户令牌,不会被覆盖
        "x-hasura-admin-secret": process.env.GATSBY_HASURA_ADMIN_SECRET,
      },
    },
  })
  .then((result) => console.log(result))
  .catch(console.log);

额外注意:Hasura认证头的选择

你的fetch请求用的是x-hasura-admin-secret,而Apollo Client调用时如果传的是Authorization,需要确认Hasura的认证配置:

  • 如果用JWT认证,需要确保Authorization头格式正确(Bearer <token>),且Hasura已配置对应的JWT密钥。
  • 如果用管理员权限,直接传x-hasura-admin-secret更直接,无需走JWT流程。

内容的提问来源于stack exchange,提问作者user1780729

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 06:31:20