GraphQL Mutation在Playground正常但客户端代码运行报错
问题1:首次调用refreshTokens返回Cannot return null for non-nullable field Mutation.refreshTokens.的根因与修复
你的issueNewToken函数存在逻辑漏洞:当请求头中不存在rt字段时,函数既没有抛出错误也没有返回值,最终返回undefined,而GraphQL schema中定义了refreshTokens返回非空的Auth!类型,因此触发非空校验报错。
修复代码:
issueNewToken: async (req) => { try { const token = req.headers.rt; if (!token) { throw new AuthenticationError("Refresh token not provided."); } const decoded = await jwt.verify(token, jwtRefreshTokenSecret); let user = await User.findById(decoded.id); if (!user) { throw new AuthenticationError("No user found."); } let tokens = await authFunctions.issueToken(user); return { ...tokens, user }; } catch (err) { throw new AuthenticationError("Invalid Refresh Token."); } },
问题2:令牌刷新成功后后续请求未携带正确Header的修复点
你的Apollo链路逻辑存在4处明显问题:
refreshTokens函数没有返回刷新后的令牌信息,导致then回调中拿到的refreshTokensResult为undefined
- 构造新请求头时未展开旧header对象,导致原有header被覆盖丢失
- 全局header链路只配置了
rt字段,未携带业务请求需要的Authorizationaccess token头
- 全局header链路只配置了
- 主业务用的Apollo Client实例未注册
errorLink和headerLink,刷新逻辑和header注入逻辑未生效
- 主业务用的Apollo Client实例未注册
分步修复代码:
1. 修复refreshTokens函数的返回值
const refreshTokens = async () => { const { data: { refreshTokens: res }, } = await renewTokenApiClient.mutate({ mutation: REFRESH_TOKENS, }); localStorage.setItem("refreshtoken", res.refreshToken); localStorage.setItem("token", res.token); // 新增返回值供后续逻辑使用 return res; };
2. 修复headerLink逻辑,同时注入两个需要的头
const headerLink = setContext((_, { headers }) => { const refreshToken = localStorage.getItem("refreshtoken"); const accessToken = localStorage.getItem("token"); return { headers: { ...headers, rt: refreshToken || "", Authorization: accessToken ? `Bearer ${accessToken}` : "", }, }; });
3. 修复errorLink中构造headers的逻辑
refreshTokens().then((refreshTokensResult) => { let headers = { // 展开旧header,不要直接写oldHeaders ...oldHeaders, Authorization: `Bearer ${refreshTokensResult.token}`, rt: refreshTokensResult.refreshToken, }; operation.setContext({ headers }); })
4. 主Apollo Client实例注册完整链路
确保你全局使用的Apollo Client按顺序注册了所有链路:
const client = new ApolloClient({ // 链路顺序:errorLink在前,然后是header注入,最后是http请求 link: from([errorLink, headerLink, createHttpLink({ uri: "/graphql" })]), cache, credentials: "include", });
内容的提问来源于stack exchange,提问作者Ruham
相关产品推荐
相关产品推荐

