当执行变更时,为什么会出现“无法为非空字段返回null”错误?

54

我在服务器端尝试使用(Apollo)GraphQL,并且遇到了一个可能很傻的问题。我正在尝试注册用户,但一直收到下方链接图片中显示的错误。问题出在哪里?请忽略非常简单的身份验证流程,因为我只是在测试GraphQl。

enter image description here

以下是相关代码片段:

Schema

export default `

type User {
    id: ID!
    name: String!
    email: String!
}

type Query {
    allUsers: [User]
  currentUser: User
}

type Mutation {
    createAccount(name: String!, email: String!, password: String!): User
    loginUser(email: String!, password: String!): User
    updatePassword(email: String!, password: String!, newPassword: String!): User
    deleteAccount(email: String!, password: String!): User
}

`

解析器

createAccount: async (
  parent,
  { name, email, password },
  { User },
  info
) => {
  try {
    // Check for invalid (undefined) credentials
    if (!name || !email || !password) {
      return 'Please provide valid credentials';
    }

    // Check if there is a user with the same email
    const foundUser = await User.findOne({ email });

    if (foundUser) {
      return 'Email is already in use';
    }

    // If no user with email create a new user
    const hashedPassword = await bcrypt.hash(password, 10);
    await User.insert({ name, email, password: hashedPassword });

    const savedUser = await User.findOne({ email });

    return savedUser;
  } catch (error) {
    return error.message;
  }
},
1个回答

55
您的解析器最大的问题是,在许多情况下,它不返回一个User对象,而是返回一个字符串。您的模式指定createAccount可以返回一个User对象或null(如果它是User!,则它是不可为空的,因此null也不是有效类型)。
当您在解析器中返回字符串时,因为它期望一个对象,所以将其强制转换为一个对象,然后开始在该对象上查找User属性(如nameemail)。这些属性不存在,因为它们是您User对象上的非空属性,对它们返回null/undefined会导致错误。
您的解析器应该只抛出需要抛出的任何错误。然后它们将作为响应中的errors数组的一部分返回。例如:
// Check if there is a user with the same email
const foundUser = await User.findOne({ email })

if (foundUser) throw new Error('Email is already in use')

// If no user with email create a new user
const hashedPassword = await bcrypt.hash(password, 10);
await User.insert({ name, email, password: hashedPassword });

const savedUser = await User.findOne({ email });

return savedUser;

现在,如果您有重复的电子邮件,响应将类似于这样:

{
  "data": {
    "createAccount": null
  },
  "errors": [
    {
      "message": "Email is already in use",
      "locations": [
        {
          "line": 4,
          "column": 3
        }
      ],
      "path": [
        "createAccount"
      ]
    }
  ]
}

如果你想在客户端上控制错误的显示方式,你可以利用 Apollo 服务器中间件的 formatErrorformatResponse 配置选项。使用自定义错误也是一个好习惯,允许你添加自定义属性如 code 在客户端更容易地识别错误类型。

最后,在你的解析器内部检查名称、电子邮件或密码是否被定义是不必要的 -- 你的模式已经将这些输入标记为非空,这意味着如果任何一个输入丢失,GraphQL 将自动返回错误。


嗨,丹尼尔,你是说 HW 不应该返回一个字符串吗?我也遇到了同样的问题。 - PureAbap
2
在我的情况下,我忘记在DataSource API回调中使用return语句。这篇文章帮助我了解了那个问题的原因!谢谢。 - Jatinder
1
@Jatinder,你刚刚通过提到return语句拯救了一条命。已经抓耳挠腮30分钟了。 我原来写的是: resolve: (source, args, { pgApi }) => {pgApi.userInfo(source.userId)} 而不是 resolve: (source, args, { pgApi }) => pgApi.userInfo(source.userId) - Hoxtygen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接