// didn't add node definitions because this problem occurs on initial data fetch
// user type
const userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: globalIdField('User'),
email: { type: GraphQLString },
posts: {
type: postConnection,
args: connectionArgs,
// getUserPosts() function is below
resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args),
},
}),
interfaces: [nodeInterface],
})
// post type
const postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
title: { type: GraphQLString },
content: { type: GraphQLString },
}),
interfaces: [nodeInterface],
})
// connection type
const {connectionType: postConnection} =
connectionDefinitions({name: 'Post', nodeType: postType})
// Mongoose query on other file
exports.getUserPosts = (userid) => {
return new Promise((resolve, reject) => {
Post.find({'author': userid}).exec((err, res) => {
err ? reject(err) : resolve(res)
})
})
}
我在浏览器控制台中收到以下警告:
查询
App
的服务器请求失败,原因如下:
- 醇>
无法读取未定义的属性“长度”
_posts40BFVD:帖子(第一名:10){
^^^
这是我获得的唯一信息,没有更多的错误或参考。可能是什么原因?
此代码来自relay-starter-kit,我只用Widget
替换了所有Post
代码。一切都与启动器几乎相同,因此我认为原因在于数据库代码。
但我看不出问题,因为getUserPosts()
返回相同的结构:对象数组..
resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args)
getUserPosts()
返回了一个承诺(阻止代码可能是一个坏主意),但没有回调。在我看来发生的事情是connectionFromArray()
继续执行代码,但它还没有来自getUserPosts()
的数据,这导致整个系统失败。
无论如何我使用Babel和JavaScript“未来功能”,因此我决定使用async
和await
。但仍有问题,getUserPosts()
返回一个空数组。
然后我发现如果在await
函数中使用async
调用另一个函数,那么另一个函数也必须是async
,否则所有await-s
都会失败。这是我的最终解决方案:
// async function that makes db query
exports.getUserPosts = async (userId) => {
try {
// db query
const posts = await Post.find({author: args}).exec()
return posts
} catch (err) {
return err
}
}
// and resolve method in Schema, also async
resolve: async (user, args) => {
const posts = await getUserPosts(user._id)
return connectionFromArray(posts, args)
}
我还不确定它是否是最好的方式。我的逻辑告诉我,我应该尽可能在Node中使用async,但我远离Node专家。当我对此有更多了解时,我会更新这个问题。
我很高兴知道使用Relay是否有更好甚至推荐的方法来处理这种数据库查询情况。
答案 0 :(得分:4)
问题在于resolve
字段的posts
功能。
resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args),
首先,getUserPosts
是一个返回promise的异步函数。在编写resolve
函数时,您必须考虑到这一点。其次,user.id
是由graphql-relay
模块的globalIdField
辅助函数为您生成的全局ID字段。该ID应转换为本地ID。
使用relay-starter-kit,您can use async
和await
。代码如下:
resolve: async (user, args) => {
let userId = fromGlobalId(user.id).id;
// convert this userId string to Mongo ID if needed
// userId = mongoose.Types.ObjectId(userId).
const posts = await getUserPosts(userId);
return connectionFromArray(posts, args),
},
在async
版getUserPosts
中,如果出现错误,则会返回err
个对象。
exports.getUserPosts = async (userId) => {
try {
// db query
const posts = await Post.find({author: args}).exec()
return posts
} catch (err) {
return err
}
}
一个好的做法是重新抛出错误或返回一个空数组。
答案 1 :(得分:3)
graphql-relay提供connectionFromPromisedArray函数,该函数等待promise解析。 resolve: (user, args) => connectionFromPromisedArray(getUserPosts(user.id), args),
这可能是处理承诺连接时最推荐/最简单的方式。
如果您的user.id
是global ID字段,则必须从base64字符串const { type, id } = fromGlobalId(user.id);
中提取实际数据库ID。
或
resolve: (user, args) => connectionFromPromisedArray(getUserPosts(fromGlobalId(user.id).id), args),
在我正在使用Relay和Mongoose的应用程序中我发现编写自己的连接实现以在数据库级别而不是在应用程序级别上处理分页和过滤更好。当我写这篇文章时,我从graffiti-mongoose中获得了很多灵感。