Node.js环境下Apollo Client请求头不生效问题排查
问题根源分析
你的Apollo Client请求头未生效的核心原因是客户端配置的authLink覆盖了你在服务端调用时传入的自定义headers,具体细节:
authLink中的setContext会在每次请求时执行,它会尝试从auth.getSession()获取token并设置Authorization头。但在服务端环境中,auth.getSession()大概率无法获取到浏览器端的用户session(因为服务端没有浏览器的存储/上下文),导致拿到的id_token是undefined,最终生成无效的Authorization: Bearer undefined头,直接覆盖了你传入的有效令牌。即使你在
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
相关产品推荐
相关产品推荐

