我在运行类似突变时遇到过一个奇怪的结果。 我只是在学习这个堆栈,我用猫鼬作为我的数据库层实现MongoDB。
我有两个简单的突变。 一个创建一个用户并将其添加到数据库(又名寄存器(中。 另一个显然是在创建初始用户对象后添加配置文件图片。
两个解析器都返回完整的用户对象,这对于更新 UI、存储等很好,我计划实施订阅,因此取回新数据很重要。
我正在使用 Graphiql 解决问题,遇到了一个奇怪的问题。 即使图像 URL 已保存到数据库,我的更新解析程序仍返回 null。 寄存器解析器返回所有用户字段。
在控制台记录解析程序中的返回对象时,两个函数都返回完整的用户对象。
当我尝试使返回对象不可为空时,我会收到错误无法为更新解析程序返回不可空的空。
我的解析器最初使用 findOneAndUpdate 和一个回调,该回调实际上确实返回了用户对象。 是的,我仍然得到空。奇怪。
我将解析器更改为更手动的方法。 我使用 findOne 查找现有用户,传入用户 ID,然后明确表示 user.profilePic = "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 个月的人来说......即我...
不管文档怎么说,当你包含一个回调时,findOneAndUpdate
返回未定义。另一方面,findOne
返回一个查询对象。这里的问题是,当你传递回调时,目的是让你处理传递给回调的值作为参数,而不是处理调用的返回值是什么。
使用 GraphQL,解析器可以返回解析为该值的值或承诺。findOne
返回的 Query 对象不是 Promise,而是"可"的",因此从某种意义上说,您以这种方式"逃脱"了。但是,我怀疑如果您查看 GraphQL 实际返回的内容,您会发现它返回的是原始用户对象,而不是保存的对象。
正如你所猜想的,有一个更好的方法:)
要让mongoose
返回承诺,您需要:
- 完全删除回调
- 将
.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!!
})
最后,如果你最终使用了回调,请注意,与 Promise 不同,它们中的 return 语句不会做任何事情(至少在这种情况下不会(。因为它们是异步的,所以你不可能以某种方式将调用它们的值返回到你的源调用(不是没有将所有内容包装在一个 Promise 中,如果你已经可以返回一个 Promise,这是一条不值得走的路径(。