为什么 Relay/GraphQL 连接中需要边?

Why are edges required in a Relay/GraphQL Connection?

在 Relay/GraphQL 架构配置中,一对多关系(带分页)在 tutorial example

中指定
type ShipConnection {
  edges: [ShipEdge]
  pageInfo: PageInfo!
}
type ShipEdge {
  cursor: String!
  node: Ship
}

然而,ShipEdge的一对一连接似乎是多余的。为什么我们不能将光标移动到 ShipConnection 并将 Ship 个 ID 的数组存储为边?

type ShipConnection {
  edges: [Ship]
  pageInfo: PageInfo!
  cursor: String!
}

一对多关系中每个 edge 需要一个额外对象的设计决定是什么?

edges 字段为您提供放置每边数据的位置。例如,您可能想在那里放置一个 creatorpriority 字段,分别描述谁添加了边以及关系的重要性。

如果您不需要这种灵活性(或通过连接获得的其他功能,例如分页),您可以使用简单的 GraphQLList 类型。有关连接和列表之间区别的更多信息,请参阅

(更新了更多解释)

在 GraphQL 中有 3 种表示数据数组的方法:

  1. 列表:当您有一个有限的关联对象列表并且您可以一次性获取所有对象时使用。在 GraphQL SDL 中,这表示为 [Ship].
  2. 节点:当您需要对列表进行分页时使用,通常是因为可能有数千个项目。请注意,这不是 Relay 规范的一部分,因此 Relay 客户端不支持(相反,您可以按照#3 中的描述将项目包裹在边缘中),但其他一些客户端(例如 Apollo)更灵活并且支持此构造(但您需要提供更多样板)。在 GraphQL 中,这将表示为 type ShipConnection { nodes: [Ship], pageInfo: PageInfo! }.
  3. Edges:当除了分页之外,您还需要为连接中的每个边缘提供额外信息时使用(阅读下文了解更多详细信息)。在 GraphQL 中,您可以将其写为 type ShipConnection { edges: [ShipEdge], pageInfo: PageInfo! }.

请注意,您的 GraphQL 服务器可能支持特定关联的所有三个选项,然后客户端选择他们想要的字段。以下是他们在一起的样子:

type Query {
  ships: [Ship]       // #1
  shipsConnection: [ShipConnection]
}

type ShipConnection {
  nodes: [Ship]       // #2
  edges: [ShipEdge]   // #3
  pageInfo: PageInfo!
}

type PageInfo {
  endCursor           // page-based pagination
  hasNextPage
}

type ShipEdge {
  cursor: String!     // edge-based pagination
  node: Ship
  // ... edge attributes
}

type Ship {
  // ... ship attributes
}

列表 (#1) 只应在您知道项目数量不会增加时使用(例如,如果您有一个 Post,您可能想要 return tags 作为列表,但您不应该使用 comments)。要在#2 和#3 之间做出决定,在普通节点上使用边有两个原因:

  • 这是特定边属性的地方。例如,如果您有一个属于许多 GroupUser,那么在关系数据库中,您将有一个包含 user_idgroup_id 的用户组 table .此 table 可以具有其他属性,如 rolejoined_at 等。GroupUserEdge 将是您可以访问这些属性的地方。

  • 给光标留个位置。 Relay 除了基于页面的分页(使用 pageInfo)之外还支持基于边缘的分页。为什么 Relay 的每条边都需要一个光标?由于 Relay 智能地合并了整个应用程序的数据需求,它可能已经与您请求的相同参数建立了连接,但其中的记录不足。要获取丢失的数据,它可以在某个边的光标之后的连接中请求数据。

    我知道这可能会造成混淆,因为数据库也有游标,而且每个查询只有一个游标。中继连接实际上不是查询,而是一组标识查询的参数。连接边缘游标是一组标识连接内位置的参数。这是比纯查询游标更高的抽象级别(请记住,即使在可能不是数据库查询或被第三方系统隐藏的连接上,边缘也需要能够识别位置)。由于这需要灵活性,一个连接的游标是不够的。

我们写了一篇关于简单 GraphQL 模式与中继特定模式之间差异的博客文章:

https://www.prisma.io/blog/connections-edges-nodes-in-relay-758d358aa4c7