架构
架构指令设计

指令设计

指令扮演着重要的角色:它们可以实现 GraphQL 规范 或 GraphQL 服务器本身原生不支持的功能。指令可以填补功能上的空白,使 API 能够满足其已知或未知的需求。

因此,指令是 GraphQL 服务器基础架构中极为重要的元素。Gato GraphQL 采用了健全且坚固的指令架构设计,使其兼具可扩展性与强大功能。

底层功能

作为一项设计决策,引擎在解析 Query 时直接依赖指令管道。因此,指令被视为底层组件,可以访问存储响应的对象。

由此,任何自定义指令都有能力修改 GraphQL 响应。

一个典型的使用场景是 @remove 指令,它允许在 Query 中指定某个字段的响应是否应省略,而不是返回 null 值(规范中有一个关于此功能的 issue)。

高效的指令调用

指令会同时接收所有受影响的对象和字段,在单次执行中完成处理。

例如,调用 Google Translate API 的次数应尽可能少。在以下 Query 中,API 仅被调用一次,包含 10 段待翻译文本(5 篇文章的 2 个字段:title 和 excerpt):

query {
  posts(pagination:{ limit: 5 }) {
    title
    excerpt
    titleES: title @translate(from: "en", to: "es")
    excerptES: excerpt @translate(from: "en", to: "es")
  }
}

在以下 Query 中,API 被调用 3 次,每种语言(西班牙语、法语和德语)各一次,每次包含 10 个字符串,所有调用并发执行:

query {
  posts(pagination:{ limit: 5 }) {
    title
    excerpt
    titleES: title @translate(from: "en", to: "es")
    excerptES: excerpt @translate(from: "en", to: "es")
    titleDE: title @translate(from: "en", to: "de")
    excerptDE: excerpt @translate(from: "en", to: "de")
    titleFR: title @translate(from: "en", to: "fr")
    excerptFR: excerpt @translate(from: "en", to: "fr")
  }
}

函数签名

以下是字段指令接口。请注意函数 resolveDirective 接收的参数:

public function resolveDirective(
  RelationalTypeResolverInterface $relationalTypeResolver,
  array $idFieldSet,
  FieldDataAccessProviderInterface $fieldDataAccessProvider,
  array $succeedingPipelineFieldDirectiveResolvers,
  array $idObjects,
  array $unionTypeOutputKeyIDs,
  array $previouslyResolvedIDFieldValues,
  array &$succeedingPipelineIDFieldSet,
  array &$succeedingPipelineFieldDataAccessProviders,
  array &$resolvedIDFieldValues,
  array &$messages,
  EngineIterationFeedbackStore $engineIterationFeedbackStore,
): void;

这些参数体现了指令的底层特性:

  • $idFieldSet:指令需要处理的每个字段的 ID 列表
  • $succeedingPipelineIDFieldSet:管道后续阶段中指令需要处理的每个字段的 ID 列表
  • $resolvedIDFieldValues:响应对象

其他参数则用于:访问 Query 变量并定义动态变量、在指令之间传递包含自定义数据的消息、触发错误和警告、识别并显示弃用信息,以及存储指标。