为什么我在进行突变时会收到“无法为非可空字段返回null”错误?

时间:2022-03-24 22:50:20

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并且一直有一个可能很愚蠢的问题。我正在尝试注册用户,但不断收到下面链接图片中显示的错误。问题是什么?忽略非常简单的auth流程,因为我只是在测试GraphQl

为什么我在进行突变时会收到“无法为非可空字段返回null”错误?

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

1 个解决方案

#1


2  

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

解析器的最大问题是,在任何数量的场景中,您返回一个字符串,而不是返回User对象。您的模式指定createAccount可以返回User或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 User properties on that object (like name and email). Those properties don't exist, and because they are non-null properties on your User object, returning null/undefined for them results in an error.

当您在解析器中返回一个字符串时,因为它期望一个对象,它会将其强制转换为一个对象,然后开始在该对象上查找用户属性(如名称和电子邮件)。这些属性不存在,并且因为它们是User对象上的非null属性,所以返回null / undefined会导致错误。

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

你的解析器应该只是抛出它需要抛出的任何错误。然后它们将作为响应中错误数组的一部分返回。例如:

// 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 formatError or formatResponse configuration options for your Apollo server middleware. It's also good practice to use custom errors, allowing you to add custom properties like code to more easily identify the error type on the client side.

如果要操纵错误在客户端上的显示方式,则应使用Apollo服务器中间件的formatError或formatResponse配置选项。使用自定义错误也是一种很好的做法,允许您添加代码等自定义属性,以便更轻松地识别客户端的错误类型。

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将自动返回错误。

#1


2  

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

解析器的最大问题是,在任何数量的场景中,您返回一个字符串,而不是返回User对象。您的模式指定createAccount可以返回User或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 User properties on that object (like name and email). Those properties don't exist, and because they are non-null properties on your User object, returning null/undefined for them results in an error.

当您在解析器中返回一个字符串时,因为它期望一个对象,它会将其强制转换为一个对象,然后开始在该对象上查找用户属性(如名称和电子邮件)。这些属性不存在,并且因为它们是User对象上的非null属性,所以返回null / undefined会导致错误。

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

你的解析器应该只是抛出它需要抛出的任何错误。然后它们将作为响应中错误数组的一部分返回。例如:

// 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 formatError or formatResponse configuration options for your Apollo server middleware. It's also good practice to use custom errors, allowing you to add custom properties like code to more easily identify the error type on the client side.

如果要操纵错误在客户端上的显示方式,则应使用Apollo服务器中间件的formatError或formatResponse配置选项。使用自定义错误也是一种很好的做法,允许您添加代码等自定义属性,以便更轻松地识别客户端的错误类型。

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将自动返回错误。