Graphql Apollo React:无法为非空值返回空值
我在运行类似的突变时,发现了返回结果中的奇怪之处。我正在学习这个堆栈,我使用 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 个月编码经验的人……比如我……
尽管文档中说了,但当您包含回调时,
findOneAndUpdate
会返回未定义。另一方面,
findOne
返回一个查询对象。这里的问题是,当您传递回调时,目的是让您将传递给回调的值作为参数处理,而不是处理调用的返回值。
使用 GraphQL,解析器可以返回一个值或解析为该值的 Promise。
findOne
返回的查询对象不是 Promise,而是“可执行的”,因此从某种意义上说,您可以通过这种方式“逃避”事情。但是,我怀疑如果你看看 GraphQL 实际返回的内容,你会发现它返回的是原始用户对象,而不是保存的对象。
正如你所猜测的,有一个更好的方法 :)
要让
mongoose
返回 Promise,你需要:
- 完全放弃回调
-
将
.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,那么这条路径并不值得走)。