javascript 为什么在进行突变时会收到“无法为不可为空的字段返回空值”错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50021669/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 08:52:28  来源:igfitidea点击:

Why am I getting a "Cannot return null for non-nullable field" error when doing a mutation?

javascriptnode.jsexpressgraphqlapollo-server

提问by Collins Orlando

I'm trying my hand at (Apollo) GraphQL on the server side and have been having a probably silly issue. I'm trying to sign up a user, but keep getting the error shown in the linked image below. What is the problem? Ignore the very simple auth flow, as I'm just testing out the GraphQl

我正在服务器端尝试使用 (Apollo) GraphQL,但遇到了一个可能很愚蠢的问题。我正在尝试注册用户,但不断收到以下链接图像中显示的错误。问题是什么?忽略非常简单的身份验证流程,因为我只是在测试 GraphQl

enter image description here

在此处输入图片说明

Here are the relevant code snippets:

以下是相关的代码片段:

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
}

`

Resolvers

解析器

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;
  }
},

回答by Daniel Rearden

The biggest problem with your resolver is that in any number of scenarios, instead of returning a Userobject, you return a string. Your schema specifies that createAccountcan return a Useror null(if it was User!, it would be non-nullable and then nullwould not be a valid type either).

你的解析器最大的问题是,在许多情况下,你不是返回一个User对象,而是返回一个字符串。您的架构指定createAccount可以返回一个Useror null(如果是User!,它将是不可为空的,然后null也不是有效类型)。

When you return a string in your resolver, because it's expecting an object, it coerces it into one and then starts looking for Userproperties on that object (like nameand email). Those properties don't exist, and because they are non-null properties on your Userobject, returning null/undefined for them results in an error.

当您在解析器中返回一个字符串时,因为它需要一个对象,它会将其强制转换为一个对象,然后开始查找User该对象的属性(如nameemail)。这些属性不存在,并且因为它们是User对象上的非空属性,为它们返回 null/undefined 会导致错误。

Your resolver should probably just throw whatever errors it needs to throw. Then they will be returned as part of the errorsarray in your response. For example:

您的解析器可能应该只抛出它需要抛出的任何错误。然后它们将作为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;

Now, if you have a duplicate email, the response will look something like this:

现在,如果您有重复的电子邮件,回复将如下所示:

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

If you want to manipulate how your errors are shown on the client, you should utilize the formatErroror formatResponseconfiguration options for your Apollo server middleware. It's also good practice to use custom errors, allowing you to add custom properties like codeto more easily identify the error type on the client side.

如果您想操纵错误在客户端上的显示方式,您应该使用Apollo 服务器中间件的formatErrorformatResponse配置选项。使用自定义错误也是一种很好的做法,允许您添加自定义属性,例如code在客户端更轻松地识别错误类型。

Lastly, it's unnecessary to check if name, email or password are defined inside your resolver -- your schema already has these inputs marked as non-null, which means GraphQL will automatically return an error if any of them are missing.

最后,没有必要检查您的解析器中是否定义了名称、电子邮件或密码——您的架构已经将这些输入标记为非空,这意味着如果其中任何一个丢失,GraphQL 将自动返回错误。