GraphQL 模式设计中的常见陷阱及如何避免

前言

GraphQL 是一种用于 API 的查询语言,它提供了一种更高效、更强大、更灵活的方式来获取和修改数据。在 GraphQL 中,模式(Schema)是定义 API 的核心部分,它描述了 API 中的所有类型、查询和变更。一个好的模式设计能够提高 API 的可用性和可扩展性,但是在设计模式时,有一些常见的陷阱需要注意。

本文将介绍 GraphQL 模式设计中的常见陷阱,并提供一些实用的技巧来避免它们。

陷阱一:过度使用嵌套类型

在 GraphQL 中,类型可以嵌套在其他类型中。这是一种非常强大的工具,可以使模式更加清晰和可读。然而,在某些情况下,过度使用嵌套类型可能会导致模式过于复杂,难以理解和维护。

例如,考虑以下的查询类型:

---- ----- -
  -------- ----- ----
-

---- ---- -
  --- ---
  ----- -------
  ------ -------
  -------- --------
-

---- ------- -
  ------- -------
  ----- -------
  ------ -------
  -------- -------
-

在这个模式中,User 类型嵌套了 Address 类型。这个设计看起来很合理,因为每个用户都有一个地址。但是,如果我们想要查询所有用户的地址,我们需要执行以下查询:

----- -
  -------- -- -
    ------- -
      ------
      ----
      -----
      -------
    -
  -
  -------- -- -
    ------- -
      ------
      ----
      -----
      -------
    -
  -
  - --- ----
-

这种查询方式会导致网络请求的数量增加,因为每个用户都需要单独查询。此外,如果我们想要过滤用户的地址,我们需要在每个用户的查询中重复相同的过滤条件。

为了避免这个问题,我们可以将 Address 类型提升为顶级类型,并使用连接(Connection)来关联它和 User 类型:

---- ----- -
  -------- ----- ----
  --------- --------
-

---- ---- -
  --- ---
  ----- -------
  ------ -------
  -------- ------------------
-

---- ------- -
  --- ---
  ------- -------
  ----- -------
  ------ -------
  -------- -------
-

---- ----------------- -
  ------ ---------------
  --------- ---------
-

---- ----------- -
  ----- --------
  ------- -------
-

---- -------- -
  ------------ --------
  ---------------- --------
  ------------ ------
  ---------- ------
-

在这个模式中,我们使用 AddressConnection 类型来表示 User 类型和 Address 类型之间的连接。这个连接包含了一个 edges 数组,其中每个元素都是一个 AddressEdge 类型,它包含了 Address 类型的实例和一个 cursor 字段。我们还定义了一个 PageInfo 类型,它包含了分页信息。

现在,我们可以通过以下方式查询所有用户和他们的地址:

----- -
  -------- -
    ----
    -----
    ------- -
      ----- -
        ---- -
          ------
          ----
          -----
          -------
        -
      -
    -
  -
-

这个查询将返回所有用户和他们的地址,而不需要执行多个网络请求。此外,我们还可以使用连接来过滤用户的地址,而不需要在每个用户的查询中重复相同的过滤条件。

陷阱二:过度使用接口和联合类型

在 GraphQL 中,接口和联合类型是表示多态类型的常用工具。它们允许我们定义一个类型,该类型可以表示多个具体类型的任何一个。这种设计非常灵活,可以帮助我们避免重复的代码和冗余的查询。

然而,在某些情况下,过度使用接口和联合类型可能会导致模式过于复杂,难以理解和维护。

例如,考虑以下的模式:

--------- ---- -
  --- ---
-

---- ---- ---------- ---- -
  --- ---
  ----- -------
  ------ -------
-

---- ---- ---------- ---- -
  --- ---
  ------ -------
  ----- -------
-

---- ----- -
  -------- ----- ----
-

在这个模式中,我们定义了一个 Node 接口,它包含一个 id 字段。然后,我们定义了两个具体类型 UserPost,它们都实现了 Node 接口。最后,我们定义了一个 Query 类型,它包含了一个 node 查询,该查询可以根据 id 获取任何 Node 类型的实例。

这个模式看起来很好,因为它允许我们通过一个查询来获取任何 Node 类型的实例。但是,如果我们想要查询所有用户和帖子的标题,我们需要执行以下查询:

----- -
  -------- -- -
    ----------
    --- -- ---- -
      ----
    -
    --- -- ---- -
      -----
    -
  -
  -------- -- -
    ----------
    --- -- ---- -
      ----
    -
    --- -- ---- -
      -----
    -
  -
  - --- ----
-

这种查询方式会导致网络请求的数量增加,因为每个节点都需要单独查询。此外,如果我们想要过滤用户或帖子的标题,我们需要在每个节点的查询中重复相同的过滤条件。

为了避免这个问题,我们可以将 UserPost 类型提升为顶级类型,并使用连接(Connection)来关联它们:

---- ----- -
  --------- --------
  --------- --------
-

---- ---- -
  --- ---
  ----- -------
  ------ -------
  ------ ---------------
-

---- ---- -
  --- ---
  ------ -------
  ----- -------
  ------- -----
-

---- -------------- -
  ------ ------------
  --------- ---------
-

---- -------- -
  ----- -----
  ------- -------
-

---- -------- -
  ------------ --------
  ---------------- --------
  ------------ ------
  ---------- ------
-

在这个模式中,我们使用 PostConnection 类型来表示 User 类型和 Post 类型之间的连接。这个连接包含了一个 edges 数组,其中每个元素都是一个 PostEdge 类型,它包含了 Post 类型的实例和一个 cursor 字段。我们还定义了一个 PageInfo 类型,它包含了分页信息。

现在,我们可以通过以下方式查询所有用户和帖子的标题:

----- -
  -------- -
    ----
    -----
    ----- -
      ----- -
        ---- -
          -----
        -
      -
    -
  -
  -------- -
    -----
    ------ -
      ----
      -----
    -
  -
-

这个查询将返回所有用户和他们的帖子,以及所有帖子和它们的作者,而不需要执行多个网络请求。此外,我们还可以使用连接来过滤用户或帖子的标题,而不需要在每个节点的查询中重复相同的过滤条件。

结论

在 GraphQL 模式设计中,过度使用嵌套类型、接口和联合类型可能会导致模式过于复杂,难以理解和维护。为了避免这个问题,我们可以使用连接来关联顶级类型,并避免过度使用接口和联合类型。

当然,这只是一些常见陷阱的解决方案之一,实际上还有很多其他的技巧和最佳实践,可以帮助我们设计出更好的 GraphQL 模式。如果你想深入了解 GraphQL 模式设计,建议阅读 GraphQL 官方文档GraphQL 官方规范

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/673cee8eface55d72056317e