如何在 GraphQL 中解析来自多个数据源的字段

Posted

技术标签:

【中文标题】如何在 GraphQL 中解析来自多个数据源的字段【英文标题】:howto resolve fields from multiple datasources in GraphQL 【发布时间】:2021-03-06 15:06:46 【问题描述】:

关于设置 GraphQL 的初学者问题。我正在努力寻找最有效的方式为我的架构设置解析器。 在我的模式中说我有一个用户类型。某些字段在一个后端 api 中解析,其他字段在另一个中解析。

type User 
   name: String
   address: String
   dateOfBirth: String
   department: String
   function: String
   manager: String

name、address 和 dateOfBirth 来自基本管理,其他字段来自组织数据库。 假设这是我的解析器:

Query 
  User(parent, args, ctx) 
    return 
       name: '....',
       address: '...',
       dateOfBirth: '.....'
    
  
 

以及特定子字段的解析器:

User 
   department(parent, args, ctx) 
   
   function(parent, args, ctx) 
   
   manager(parent, args, ctx) 
   

如果用户请求所有字段,这将导致 4 个请求。最后三个请求可能是一次获取所有字段的 1 个请求。我当然希望这是 2 个请求:一个用于基本信息,一个用于组织 API。这可能会导致这种模式:

type User 
   name: String
   address: String
   dateOfBirth: String
   organisation: Organisation
 
type Organisation 
   department: String
   function: String
   manager: String

和我的子字段解析器:

User 
   organisation 
      return 
         department: '...',
         function: '...'
         manager: '...'
      
   
 

现在对组织 API 的请求只被请求一次。但是,模式看起来很奇怪:这些字段应该是子对象的一部分。如果我们例如要将管理器数据移动到它自己的 API,如果我们将其移出子对象组织,架构将会中断。 我尝试使用数据加载器解决这个问题,但是在尝试了一些代码示例之后,我认为数据加载器更多的是关于 n+1 问题,围绕相同对象类型的键,而不是批处理不同的字段。 那么,解决这个问题的正确方法是什么?

【问题讨论】:

User 的解析器上,它必须返回一个 User 类型的对象,只需在此处进行两个 API 调用并组成对象被退回。然后这个 User 对象被传播到子解析器,它已经被解析了,所以你只需要从父解析器获取字段并返回它(一些规范默认这样做,所以你可以省略每个字段的解析器) @AlbertAlises - 问题是当不同的领域部门、职能部门和经理,你希望这些是一个 sep。 api 调用,仅在客户端请求时执行。这将建议将它们放在一个单独的子字段中,以便将它们分组,或者为所有字段单独设置解析器。在第一种情况下,您将根据解析字段的方式进行建模,我认为这不是好的做法。第二个选项是可取的,但比请求的每个字段都会导致单独的 api 调用。所以我需要一些请求字段的批处理,导致 1 api 调用。 【参考方案1】:

在父解析器上解析(返回)的任何内容都会传播到子解析器。所以在 User 你可以这样做:

Query 
  User(parent, args, ctx) 
    //Call your API here that returns the organization fields
    const organization = getOrganizationFields();
    return 
       ...organization
       name: '....',
       address: '...',
       dateOfBirth: '.....'
    
  
 

然后在子解析器(字段解析器)上,该信息将可用,因此您可以这样做:

User 
   department(parent, args, ctx) 
     return parent.department
   
   manager(parent, args, ctx) 
     return parent.manager
   

graphql-js 等一些实现使这些“单一”解析器隐含,因此您不必编写它们。当然,如果你想批量/缓存 API 请求,你应该使用DataLoader

【讨论】:

【参考方案2】:

这可能会导致这种模式:

type User 
   name: String
   address: String
   dateOfBirth: String
   organisation: Organisation
 
type Organisation 
   department: String
   function: String
   manager: String

和我的子字段解析器:

User 
   organisation 

      // fetch from organisation service using parent.id
      
      return 
         department: '...',
         function: '...'
         manager: '...'
      
   
 

现在对组织 API 的请求只被请求一次。

您正朝着正确的方向前进,因为通常 graphql 只允许您请求所需的内容。

Albert 的 [用户级解析器] 一个数据源可能会发生过度获取(一个带有连接的 SQL 请求)。

但是,架构看起来很奇怪:这些字段应该是子对象的一部分。

可以,但不代表不能这样解决。

如果我们例如要将经理数据移动到它自己的 API, 如果我们将其移出子对象 Organisation,架构将会中断。

是的......也许你应该有更多的结构模式

...你可能是

使用现有服务/BE 视角查看此架构将重大更改/API 版本控制问题混入此上下文中

...但是您已经可以定义“正确的架构”,f.e.

type User 
  name: String
  address: String
  dateOfBirth: String
  role: Role
 
type Role 
  organisation: Organisation
  function: String

type Organisation 
  department: String
  manager: User

...假设每个用户只有一个角色...否则使用数组

在这种情况下,roleorganisation 子对象可以通过一次调用组织服务来解决 - yust

return 
  function: "team lead",
  organisation: 
    id: "someDeptID",
    department: "some",
  

...如果您查询需要manager 字段,那么它将(应该)由Organisation.manager 解析器解析。当然,如果数据已经获取/可用,您可以将其返回(在 role 解析器中)。

【讨论】:

以上是关于如何在 GraphQL 中解析来自多个数据源的字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GraphQL 和 ApolloStack 解析联合/接口字段

多个字段解析器使用不同的查询参数解析相同的 REST API

如何在 Apollo GraphQL Server 中添加字段解析器

如何使用 type-graphql 解析器函数获取 graphql 后端中的选定字段?

如何使用字段作为来自一个 graphql 查询的过滤器来获取位于单独查询中的流体图像?

从 graphql 获取解析器级别的请求字段