结合接口和具体类型的解析器

时间:2019-05-29 19:29:21

标签: graphql apollo-server

由于某些原因,我很难弄清楚如何为GraphQL接口和实现该接口的类型组合解析器。

说我有以下模式:

interface IPerson {
  id: ID!
  firstName: String!
  lastName: String!
}

type ClubMember implements IPerson {
  ...IPerson fields
  memberType: String!
  memberSince: DateTime!
}

type StaffMember implements IPerson {
  ...IPerson fields
  hireDate: DateTime!
  reportsTo: StaffMember
}

extend type Query {
  people(ids: [Int!]): [IPerson]
}

包含所有字段的完整ClubMember查询,例如:

query {
  people(ids: [123456,234567,345678]) {
    id
    firstName
    lastName
    ... on ClubMember {
      memberType
      memberSince
    }
  }
}

将产生如下响应:

[
  {
    "id": 123456,
    "firstName": "Member",
    "lastName": "McMemberface",
    "memberType": "VIP",
    "memberSince": "2019-05-28T16:05:55+00:00"
  },
  ...etc.
]

我已将makeExecutableSchema()中的apollo-serverinheritResolversFromInterfaces: true一起使用,并且我希望能够通过让模型类支持{{ 1}},IPerson等返回仅具有与每种类型相关的字段的对象,即ClubMember的模型类仅获取IPerson等所需的字段。 ,上面的响应将执行2条SQL语句:

IPerson

SELECT id, firstName, lastName FROM Contacts WHERE id IN(?);

当然,我可以通过在数据库级别执行JOIN来在一条SQL语句中获取所有数据,但我确实希望有一种(只有一种)方式来解决SELECT contactId, memberType, memberSince FROM Members WHERE contactId IN(?); 所需的字段,并让其他类型的数据使用自己的解析器来扩充该数据。

我的问题是,我是否需要自己在IPerson查询类型的解析器中将结果对象“连接”在一起?例如

people

或者阿波罗有什么办法可以为我们处理这个问题?我想要类似apollo-resolvers的东西吗? docs on unions and interfaces并不是超级有帮助;我在const resolvers = { Query: { people: function( parent, args, context, info ) { let persons = context.models.Person.getByIds( args.ids ); let members = context.models.Member.getByIds( args.ids ); /* return an array of {...person, ...member} where person.id === member.id */ } } } 上有__resolveType,但是文档未指定如何解析每种具体类型的字段。是否有更好的方法可以使用Dataloader或其他方法来实现此目标?

我认为this question与我的问题有关,因为如果查询不通过片段请求该类型的任何字段,我不想获取具体类型的数据。 Github上还有this issue

非常感谢!

编辑: IPerson如下所示:

__resolveType

1 个答案:

答案 0 :(得分:0)

这个问题确实不是特定于Apollo Server甚至是GraphQL的-查询多个表并获得一组结果,尤其是在处理多个数据模型时,将变得棘手。

当然,您可以分别查询每个表并合并结果,但这并不是特别有效。我认为到目前为止,处理这种情况的最简单方法是在数据库中创建视图,例如:

CREATE VIEW people AS
    SELECT club_members.id           AS id,
           club_members.first_name   AS first_name,
           club_members.last_name    AS last_name,
           club_members.member_type  AS member_type,
           club_members.member_since AS member_since,
           null                      AS hire_date,
           null                      AS reports_to,
           'ClubMember'              AS __typename
    FROM club_members
    UNION
    SELECT staff_members.id         AS id,
           staff_members.first_name AS first_name,
           staff_members.last_name  AS last_name,
           null                     AS member_type,
           null                     AS member_since,
           staff_members.hire_date  AS hire_date,
           staff_members.reports_to AS reports_to
           'StaffMember'            AS __typename
    FROM staff_members;

您也可以只使用一个表,但是视图允许您将数据保留在单独的表中并一起查询。现在,您可以为视图添加数据模型,并使用该模型查询所有“人员”。

注意:为方便起见,我在此处添加了__typename列-通过返回__typename,您可以省略指定自己的__resolveType函数的作用-GraphQL将分配适当的类型在运行时为您服务。