开发者问题收集

Graphql Apollo React:无法为非空值返回空值

2017-08-21
1195

我在运行类似的突变时,发现了返回结果中的奇怪之处。我正在学习这个堆栈,我使用 mongoose 作为我的数据库层来实现 mongodb。

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

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

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

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

当我尝试使返回对象不可为空时,我会收到错误“无法为更新解析器返回不可为空的空值”。

我的解析器最初使用 findOneAndUpdate 和一个回调,该回调确实返回了用户对象。是的,我仍然得到 null。很奇怪。

我将解析器更改为更手动的方法。我使用 findOne 查找现有用户并传入用户 ID,然后明确说明 user.profilePic =“图片的网址”并调用整个用户对象的保存并在回调中返回用户对象。然后,成功了!

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

//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 个月编码经验的人……比如我……

1个回答

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

使用 GraphQL,解析器可以返回一个值或解析为该值的 Promise。 findOne 返回的查询对象不是 Promise,而是“可执行的”,因此从某种意义上说,您可以通过这种方式“逃避”事情。但是,我怀疑如果你看看 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 不同,它们内部的返回语句不会执行任何操作(至少在这种情况下不会)。因为它们是异步的,所以您无法以某种方式将它们调用的值返回到您的原始调用(除非将所有内容包装在 Promise 中,如果您已经可以返回 Promise,那么这条路径并不值得走)。

Daniel Rearden
2017-08-21