【问题标题】:Connection error with Relay继电器连接错误
【发布时间】:2016-09-29 17:19:46
【问题描述】:
// 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的服务器请求失败,原因如下:

  1. 无法读取未定义的属性“长度”

    _posts40BFVD:posts(first:10) {

    ^^^


这是我得到的唯一信息,没有更多的错误或参考。可能是什么原因?

此代码来自relay-starter-kit,我只是将所有Widget 代码替换为Post。一切都与启动器中的几乎相同,因此我认为原因是在数据库代码的某个地方。

但我看不到问题,因为getUserPosts() 返回相同的结构:对象数组..


出了什么问题?

resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args)

getUserPosts() 返回了一个承诺(阻塞代码可能是个坏主意)但没有回调。我认为发生的情况是connectionFromArray()继续执行代码,但它还没有来自getUserPosts()的数据,导致整个系统失败。

一种可能的解决方案

无论如何我都使用 Babel 和 JavaScript “未来功能”,因此我决定使用 async 和 await。但是还是有问题,getUserPosts() 返回了一个空数组。

然后我发现如果在async 函数中使用await 调用另一个函数,则另一个函数也必须是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 中尽可能多地使用异步,但我远非 Node 专家。当我了解更多信息时,我会更新问题。

我很高兴知道是否有更好甚至推荐的方法来使用 Relay 处理这种数据库查询情况。

【问题讨论】:

    标签: relayjs graphql-js


    【解决方案1】:

    graphql-relay 提供了一个 connectionFromPromisedArray 函数,它会一直等到 promise 解决。 resolve: (user, args) => connectionFromPromisedArray(getUserPosts(user.id), args), 这可能是处理承诺连接时最推荐/最简单的方式。

    如果您的user.id 是global ID 字段,那么您必须从base64 字符串const { type, id } = fromGlobalId(user.id); 中提取真实的DB ID。
    或
    resolve: (user, args) => connectionFromPromisedArray(getUserPosts(fromGlobalId(user.id).id), args),

    在我使用 Relay 和 Mongoose 的应用程序中,我发现编写自己的连接实现来处理数据库级别而不是应用程序级别的分页和过滤更好。我写的时候从graffiti-mongoose那里得到了很多灵感。

    【讨论】:

      【解决方案2】:

      问题在于posts 字段的resolve 函数。

      resolve: (user, args) => connectionFromArray(getUserPosts(user.id), args),
      

      首先,getUserPosts 是一个异步函数,它返回一个承诺。在编写 resolve 函数时必须考虑到这一点。其次,user.id 是graphql-relay 模块的globalIdField 辅助函数为您生成的全局ID 字段。该 ID 应转换为本地 ID。

      使用继电器启动器套件,您可以 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
        }
      }
      

      一个好的做法是重新抛出错误或返回一个空数组。

      【讨论】:

      • 感谢您帮助我理解它。这个相对简短的回答令人大开眼界。这在一定程度上解决了这个问题,但现在getUserPosts 出于某种原因返回一个空数组,edges 也是空数组。我在await 行之后使用了console.log。有什么想法吗?
      • 我想通了! :-) async 和 await 仅在您等待的功能(在我的示例中为 getUserPosts )也是 async 时才有效。谢谢您的努力!你给我指出了正确的方向。我猜async 是使用 Node 的方式,你不觉得吗?我会更新问题。
      • 忘记将 Relay 全局 ID 添加到本地 ID 转换部分。修改了答案。请检查并告诉我们。
      • @Solo,您可以使用try ... catch 块来处理async 函数中的错误。您会在 Internet 上找到许多带有错误处理的 async await 示例。顺便说一句,如果您在异步函数中返回 Promise,则无需将 async 放入该函数的声明中。
      猜你喜欢
      • 1970-01-01
      • 2017-02-26
      • 2017-10-28
      • 1970-01-01
      • 2022-07-28
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      相关资源
      最近更新 更多