Architecture 对于文档,我们急切地获取ProjectResolver中的所有内容,然后让GraphQL服务器轻松地解析文档字段。这似乎很好,但我们已经将文档解析的负担转移到了项目解析器上。让我们添加一个具有字段createdDocuments:[文档!]的类型用户 type User { id: ID! name: String! createdDocuments: [Document!]! }

Architecture 对于文档,我们急切地获取ProjectResolver中的所有内容,然后让GraphQL服务器轻松地解析文档字段。这似乎很好,但我们已经将文档解析的负担转移到了项目解析器上。让我们添加一个具有字段createdDocuments:[文档!]的类型用户 type User { id: ID! name: String! createdDocuments: [Document!]! },architecture,graphql,apollo,dataloader,Architecture,Graphql,Apollo,Dataloader,在用户上查询创建的文档时会发生什么情况?没有任何帮助,除非我们让UserResolver也获取文档数据通过允许家长成为其子女的唯一数据源,我们强制所有未来的家长也这样做。这使得我们的GraphQL API脆弱,难以维护和扩展。如果我们只是让ProjectResolver变懒,只返回最小值,然后强制DocumentResolver完成所有与文档相关的工作,那么我们就没有这个问题 从那两次往返DB的过程中,仍然会有一种发痒的感觉。您可以通过更多地使用数据加载器并使用缓存启动,走中间路线。该实现有一个

在用户上查询创建的文档时会发生什么情况?没有任何帮助,除非我们让UserResolver也获取文档数据通过允许家长成为其子女的唯一数据源,我们强制所有未来的家长也这样做。这使得我们的GraphQL API脆弱,难以维护和扩展。如果我们只是让ProjectResolver变懒,只返回最小值,然后强制DocumentResolver完成所有与文档相关的工作,那么我们就没有这个问题

从那两次往返DB的过程中,仍然会有一种发痒的感觉。您可以通过更多地使用数据加载器并使用缓存启动,走中间路线。该实现有一个名为prime()的方法,该方法允许您将数据播种到加载程序的缓存中。如果您使用的是一组数据加载器,那么可能会有多个加载器在不同的上下文中引用相同的对象。(如果您使用Apollo客户端进行前端工作,这应该让您感到熟悉)。当您在一个上下文中获取某个对象时,作为后处理步骤,只需为其他上下文初始化它

当您为一个项目获取文档列表时,请继续并急切地获取内容,但使用该列表的结果来启动DocumentLoader。现在,当DocumentResolver启动时,它将准备好所有这些数据,但如果没有预取的结果,它仍然是自给自足的。您必须根据应用程序的需要在何时执行此操作时使用最佳判断。您还可以使用Daniel Rearden的建议,并使用GraphQLResolveInfo有条件地决定像这样预取,但请确保不要陷入执行微优化的泥潭中

假设有两个数据加载程序:ProjectDocumentsLoader和DocumentLoader。ProjectDocumentsLoader可以将其结果作为后处理步骤初始化DocumentLoader。我喜欢将数据加载器封装在轻量级抽象中,以处理预处理和后处理


类加载器{
负载(id){
让results=wait this.loader.load(id)
返回此。后处理(结果);
}
后处理(数据){
返回数据;
}
素数(键、值){
this.dataLoader.prime(key,value);
}
}
类ProjectDocumentsLoader扩展加载程序{
构造函数(上下文){
this.context=上下文;
this.loader=newdataloader(/*函数用于按项目获取文档集合*/);
}
后处理(文档){
documents.forEach(doc=>this.context.documentLoader.prime(doc.id,doc));
归还文件;
}
}
类DocumentLoader扩展了Loader{
构造函数(上下文){
this.context=上下文;
this.loader=newdataloader(/*函数通过id*/获取文档);
}
}

所以最后的答案是:你的GraphQL解析器应该是超级懒惰的,只要它是一个优化,而不是真相的来源,就可以选择预取。

我没有做你在第一段中描述的事情,所以我将跳过它。在第二段到最后一段中,我的意思是,解析程序越不懒惰,它的子解析程序就越依赖于父解析程序。看起来最灵活的GraphQLAPI应该是每个解析器都知道如何获取域对象和提取所需字段的API,但是您得到的粒度越大,优化查询就越困难。我将稍微玩一下我的数据获取层。也许你可以用一个更详细的例子来更新你的问题。我没有做你在第一段中描述的事情,所以我将跳过这个。在第二段到最后一段中,我的意思是,解析程序越不懒惰,它的子解析程序就越依赖于父解析程序。看起来最灵活的GraphQLAPI应该是每个解析器都知道如何获取域对象和提取所需字段的API,但是您得到的粒度越大,优化查询就越困难。我将稍微玩一下我的数据获取层。也许你可以用一个更详细的例子来更新你的问题
{
  project(id: "cool-beans") {
    documents {
      id
      content
    }   
  }
}
Assume the user state is processed outside of the GraphQL context and injected into the context.
SELECT id, name, content FROM "Documents" JOIN permisssions, etc etc
function resolveFullName ({ first_name, last_name }) => {
  return `${first_name} ${last_name}`;
}
type User {
  id: ID!
  name: String!
  createdDocuments: [Document!]!
}