【问题标题】:Graphql Apollo React: Can't Return null for non nullableGraphql Apollo React:不能为不可空返回空
【发布时间】:2018-01-28 23:36:36
【问题描述】:

当我运行类似的突变时,我遇到了一个奇怪的返回结果。我只是在学习这个堆栈,我用 mongoose 作为我的数据库层来实现 mongodb。

我有两个简单的突变。一个人创建一个用户并将其添加到数据库中,也就是注册。另一个显然是在创建初始用户对象后添加了个人资料图片。

两个解析器都返回完整的用户对象,这对于更新 ui、商店等非常有用,我正计划实施订阅,因此获取新数据很重要。

我正在使用 graphiql 解决问题,但遇到了一个奇怪的问题。即使图像 url 已保存到数据库,我的更新解析器仍返回 null。注册解析器正在返回所有用户字段。

在控制台记录解析器中的返回对象时,两个函数都返回一个完整的用户对象。

当我试图使返回对象不可为空时,我会得到错误不能为更新解析器的不可空返回 null。

我的解析器最初使用带有回调的 findOneAndUpdate,该回调实际上返回了用户对象。是的,我仍然是空的。很奇怪。

我将解析器更改为更手动的方法。我通过传入用户 ID 的 findOne 查找现有用户,然后明确地说 user.profilePic = "url of pic" 并调用整个用户对象的保存并在回调中返回用户对象。和繁荣,这行得通!

那是什么原因造成的呢?我最初的感觉是这与时间有关,也就是不等待回调....我真的不明白为什么我的第一种方法不起作用而我的第二种方法不起作用。我将为两者附上代码,也许有人对时间有更深入的了解,或者可能异步函数可以加入。也许我需要将我的风格从回调更新为承诺或异步等待。

//this one doesnt work    
addProfilePic: (root, { input }, context) => {
  let update = { profilePic: input.profilePic };
  let query = { id: input.id };
  let options = { new: true, upsert: true};
  let callback = ((err, user) => {
    if(err) console.log(err.message);
    return user;
  })
  return updatedUser = User.findOneAndUpdate(query, update, options, callback)
}                    

//this one works, but returns old user object to client...
//so really, no, it doesn't work
addProfilePic: (root, { input }, context) => {
  return User.findOne({id: input.id}, ((err,user) => {
    if(err)console.log(err);
    if(user){
      user.profilePic = input.profilePic;
      user.save((err) => {
        if(err)console.log(err);
        console.log(user);
        return user;
      })
    }
  })
})

注意:传入上下文,当我实际实现时,我将从上下文中获取 id,其中包含登录时的用户。 注意:这些很快就会成为很酷的工具,但还有很多东西要学,尤其是对于有 3 个月总编码经验的人...即我...

【问题讨论】:

标签: mongodb mongoose graphql graphql-js


【解决方案1】:

不管文档怎么说,当您包含回调时,findOneAndUpdate 返回未定义。另一方面,findOne 返回一个 Query 对象。这里的问题是,当您传递回调时,目的是让您处理作为参数传递给回调的值,而不是处理调用的返回值。

使用 GraphQL,解析器可以返回一个值或解析为该值的 Promise。 findOne 返回的 Query 对象不是 Promise,而是“thenable”,因此从某种意义上说,您正在“逃避”以这种方式做事。但是,我怀疑如果您查看 GraphQL 实际返回的内容,您会发现它返回的是原始用户对象,而不是保存的对象。

正如你猜想的那样,有更好的方法:)

要让mongoose 返回一个 Promise,您需要:

  1. 完全放弃回调
  2. .exec() 附加到通话末尾

现在您的解析器如下所示:

addProfilePic: (root, { input }, context) => {
  let update = { profilePic: input.profilePic };
  let query = { id: input.id };
  let options = { new: true, upsert: true};
  return User.findOneAndUpdate(query, update, options).exec()
}

一些额外的说明可以让你走上正确的道路:

你会注意到我在上面的代码中没有做任何错误处理。这是因为 GraphQL 实际上会为您捕获这些错误并将它们包含在响应中。但是,如果您想提供额外的错误信息,混淆返回给客户端的详细信息,您可以将 catch() 附加到您的调用中,在里面修改您的错误然后扔回去。

现在您正在返回一个承诺,您可以使用then() 如果您需要处理查询结果,只要记住返回里面的值!

return User.findOneAndUpdate(query, update, options).exec()
  .then(user => {
    console.log(user)
    // you could modify the object being handed to GraphQL here
    return user // IF you use a then, make sure you return the value!!
  })

最后,如果您最终使用回调,请注意,与 Promises 不同的是,它们内部的 return 语句不会做任何事情(至少在这种情况下不会)。因为它们是异步的,所以您无法以某种方式将它们被调用的值返回给您的原始调用(如果没有将所有内容包装在 Promise 中,那么如果您已经可以返回 Promise,那么这条路径就不值得采用)。

【讨论】:

  • 你是对的,我意识到我正在返回我原来的 findOne 用户对象,即使 graphiql 显示的是正确的最新数据。我也喜欢这种错误模式,我这样做的方式变得乏味。我发现很难找到这个堆栈的标准模式,有点只是随心所欲。非常有用的建议。
  • @benjaminadk This is a curated list of GraphQL examples。里面有几个使用猫鼬,你可能会觉得有帮助。
猜你喜欢
  • 2019-10-25
  • 2018-04-10
  • 2021-08-18
  • 2017-07-13
  • 2021-10-07
  • 2019-05-28
  • 2019-07-08
  • 2019-01-26
  • 2023-03-28
相关资源
最近更新 更多