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

如何修复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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 11:17:34