如何修复Apollo Server向前端传递错误信息的问题
问题根源分析
你的核心问题出在后端mutation使用了回调式异步操作,导致错误无法被Apollo正确捕获,同时mutation函数未返回Promise,使得Apollo认为你没有返回符合类型定义的结果——这就是为什么控制台能看到你自定义的错误,但前端拿到的却是GraphQL error: Cannot return null for non-nullable field Mutation.registerUser的原因。
回调函数里的throw只会在回调上下文里抛出,根本传不到Apollo的错误处理链;而且你的mutation函数没等异步操作完成就结束了,Apollo得不到预期的Response类型返回值,自然触发了非空字段的报错。
分步解决方案
1. 把回调式异步改成async/await(关键修复)
Mongoose和bcrypt都支持Promise形式的调用,我们用async/await重构后端mutation,让异步操作的错误能被Apollo正确捕获:
// 后端mutation修改后代码 Mutation: { registerUser: async (_, { registerInput }) => { // 标记为async函数 const { username, password } = registerInput; const saltRounds = 12; // 基础校验 if (!username || !password) { throw new Error('Username and password are required!'); } if (password.length < 6) { throw new Error('Password must be minimum 6 characters!'); } // 用await替代findOne回调 const existingUser = await UserModel.findOne({ username }); if (existingUser) { throw new Error('That username is already in use!'); } // bcrypt异步操作改用Promise版本 const salt = await bcrypt.genSalt(saltRounds); const hash = await bcrypt.hash(password, salt); // 保存新用户 const newUser = new UserModel({ username, password: hash, }); await newUser.save(); return { message: 'Registered successfully' }; }, }
2. 用Apollo专用错误类型优化体验(可选但推荐)
Apollo Server提供了UserInputError这类专用错误类型,能自动标记错误为客户端输入问题,前端更容易识别和处理:
首先导入错误类型:
const { UserInputError } = require('apollo-server');
然后把抛出的错误替换为:
if (!username || !password) { throw new UserInputError('Username and password are required!'); } if (password.length < 6) { throw new UserInputError('Password must be minimum 6 characters!'); } if (existingUser) { throw new UserInputError('That username is already in use!'); }
3. 前端正确捕获错误
现在后端的错误会被Apollo正常传递到前端,你可以在mutation的catch块里拿到自定义的错误信息,而不是被当成成功数据:
// React组件中使用useMutation的示例 const [registerUser, { loading }] = useMutation(REGISTER_USER); const handleSubmit = async (formValues) => { try { const { data } = await registerUser({ variables: { registerInput: formValues } }); // 成功逻辑:页面跳转、提示用户等 console.log(data.registerUser.message); } catch (err) { // 这里拿到后端抛出的自定义错误 console.error(err.message); // 会显示"That username is already in use!"等信息 // 把错误展示给用户,比如更新state显示错误提示 } };
内容的提问来源于stack exchange,提问作者Person
相关产品推荐
相关产品推荐

