【问题标题】:howto resolve fields from multiple datasources in GraphQL如何在 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 调用。

标签: javascript graphql


【解决方案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 解析器中)。

    【讨论】:

      猜你喜欢
      • 2022-07-24
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      相关资源
      最近更新 更多