掘金 后端 ( ) • 2024-04-07 13:54

GraphQL 查询最佳实践:操作命名、GraphQL 变量等 创建查询和变更时,遵循以下最佳实践,以充分利用 GraphQL 和 Apollo 工具。

为所有操作命名

这两个查询获取相同的数据:

# 推荐 ✅
query GetBooks {
  books {
    title
  }
}

# 不推荐 ❌
query {
  books {
    title
  }
}

第一个查询被命名为 GetBooks。第二个查询是匿名的。

您应该为应用中的每个 GraphQL 操作定义一个名称。这样做提供以下好处:

  • 您可以为自己和团队成员澄清每个操作的目的。
  • 当在单个查询文档中组合多个操作时,可以避免出现意外错误(匿名操作只能单独出现)。
  • 您可以改善客户端和服务器代码中的调试输出,帮助您准确识别导致问题的操作。
  • Apollo Studio 提供有用的操作级别指标,这需要命名操作。

使用 GraphQL 变量提供参数

这两个查询都可以获取 ID 为 "5" 的 Dog 对象:

# 推荐 ✅
query GetDog($dogId: ID!) {
  dog(id: $dogId) {
    name
    breed
  }
}

# 不推荐 ❌
query GetDog {
  dog(id: "5") {
    name
    breed
  }
}

第一个查询使用变量($dogId)作为 dog 字段的必需参数的值。这意味着您可以使用该查询获取任何 ID 的 Dog 对象,使其更具可重用性。

您可以像这样将变量值传递给 useQuery(或 useMutation):

dog.tsx
const GET_DOG = gql`
  query GetDog($dogId: ID!) {
    dog(id: $dogId) {
      name
      breed
    }
  }
`;

function Dog({ id }) {
  const { loading, error, data } = useQuery(GET_DOG, {
    variables: {
      dogId: id
    },
  });
  // ...渲染组件...
}

硬编码 GraphQL 参数的缺点

除了可重用性之外,硬编码参数与变量相比还有其他缺点:

减少缓存效果

如果两个相同的查询有不同的硬编码参数值,它们被您的 GraphQL 服务器的缓存视为完全不同的操作。缓存使您的服务器能够跳过之前遇到的操作的解析和验证,提高性能。

服务器端缓存还支持自动持久化查询和联邦网关中的查询计划等功能。硬编码参数减少了这些功能的性能提升,并占用了缓存中有用的空间。

减少信息隐私

GraphQL 参数的值可能包含敏感信息,例如访问令牌或用户的个人信息。如果此信息包含在查询字符串中,则会与该查询字符串的其余部分一起缓存。

变量值不包含在查询字符串中。您还可以指定将哪些变量值(如果有的话)包含在向 Apollo Studio 报告的指标中。

仅查询您需要的数据,且在需要时查询

GraphQL 相比传统的 REST API 最大的优势之一是其对声明式数据获取的支持。每个组件都可以(也应该)准确查询它渲染所需的字段,不会有多余的数据通过网络发送。

如果相反,您的根组件执行一个单一的、庞大的查询以获取其所有子组件的数据,它可能会代表当前状态下甚至未渲染的组件查询。这可能导致响应延迟,并且极大地降低服务器端响应缓存重用查询结果的可能性。

在大多数情况下,如下查询应该被分解为多个查询,并分配给适当的组件:

  • 如果您有始终一起渲染的组件集合,您可以使用片段在它们之间分配单个查询的结构。参见片段共定位。
  • 如果您正在查询的列表字段返回的项目数量超过了您的组件需要渲染的数量,您应该对该字段进行分页。

分别查询全局数据和用户特定数据

某些字段无论哪个用户查询都返回完全相同的数据:

# 返回周期表的所有元素
query GetAllElements {
  elements {
    atomicNumber
    name
    symbol
  }
}

其他字段根据查询它们的用户返回不同的数据:

# 返回当前用户的文档
query GetMyDocuments {
  myDocuments {
    id
    title
    url
    updatedAt
  }
}

为了提高服务器端响应缓存的性能,只要可能,就在单独的查询中获取这两种类型的字段。通过这样做,您的服务器可以为 GetAllElements 之类的查询缓存单个响应,同时为执行 GetMyDocuments 的每个用户缓存单独的响应。