哪个是正确的解析器函数方法?

Which is the correct resolver function approach?

我想阐明我应该为 Apollo + GraphQL 中的解析器函数使用哪种方法

让我们假设以下架构:

type Post {
  id: Int
  text: String
  upVotes: Int
}

type Author{
  name: String
  posts: [Post]
}

schema {
  query: Author
}

ApoloGraphql tutorial 建议解析器映射如下:

{Query:{
    author(_, args) {
      return author.findAll()
          }
    }
},
Author {
   posts: (author) => author.getPosts(),
}

据我所知,关于帖子的每一个逻辑,例如get author with posts where the count of post upVotes > args.upVotes,必须在author方法中处理。这为我们提供了以下解析器映射:

{Query:{
    author(_, args) {
      return author.findAll({
              include:[model: Post]
              where: {//post upVotes > args.upVotes}
                })
             }
},
Author {
   posts: (author) => author.getPosts(),
}

调用 author,将首先 select 在一个联合查询中有帖子的作者,其中帖子大于 args.upVotes。然后它将再次 select 该作者的帖子,因为 Author ... getPosts()

在一个额外的查询中

从技术上讲,我可以通过删除 Author 来达到相同的结果,因为帖子已经包含在小型 author 方法中。

我有以下问题:

  1. 我需要这个声明吗?在哪些情况下?

    Author { posts: (author) => author.getPosts(), }

  2. 如果没有,那么我如何才能知道是否请求了 posts 字段,以便 我可以有条件地包含帖子,不仅取决于 参数,还有请求的字段?

  3. 如果是,哪些帖子会包含最终结果?来自的帖子 include 语句,或 getPosts()?

您在问题中包含的解析器映射无效。我假设你对 Author 类型的意思是这样的:

Author {
  posts: (author) => author.getPosts(),
}

只要您的 author 查询 总是 解析为包含 posts 属性 的对象数组,那么您正确的想法是在 Author 类型的 posts 字段中包含客户解析器是没有意义的。在这种情况下,您的查询解析器已经填充了所有必需的字段,我们无需执行任何其他操作。

GraphQL 使用默认解析器查找传递给解析器的父(或根)对象的属性,并在它们与正在解析的字段名称匹配时使用这些属性。因此,如果 GraphQL 正在解析 posts 字段,并且 posts 没有解析器,默认情况下它会查看它正在处理的 Author 对象,如果有 属性 在它的名称 posts 上,它将字段解析为它的值。

当我们提供自定义解析器时,该解析器会覆盖默认行为。因此,如果您的解析器是,例如:

posts: () => []

那么 GraphQL 将始终 return 一组空的帖子,即使 return 由 author.findAll() 编辑的对象包含帖子。

那么什么时候需要包含 posts 的解析器?

如果您的 author 解析器没有 "include" 帖子,但客户请求了该字段。正如您所说,问题是在某些情况下我们可能会进行不必要的额外调用,具体取决于您的 author 解析器是否 "includes" 帖子。你可以通过做这样的事情来解决这个问题:

posts: (author) => {
  if (author.posts) return author.posts
  return author.getPosts()
}
// or more succinctly
posts: author => author.posts ? author.posts : author.getPosts()

这样,我们只在确实需要获取帖子时才调用 getPosts。或者,您可以省略 posts 解析器并在 author 解析器中处理它。我们可以查看传递给解析器的第四个参数以获取有关请求的信息,包括请求了哪些字段。例如,您的解析器可能如下所示:

author: (root, args, context, info) => {
  const include = []
  const requestedPosts = info.fieldNodes[0].selectionSet.selections.includes(s => s.name.value === 'posts'
  if (requestedPosts) include.push(Post)
  return Author.findAll({include})
}

现在,如果客户明确要求,您的解析器将只包含每位作者的帖子。提供给解析器的 AST 树对象解析起来很麻烦,但是有一些库(比如 this one)可以帮助解决这个问题。