利用内置的GraphQL指令工作的方法

361 阅读5分钟

指令是GraphQL最好的--也是最不言而喻的--功能之一。

让我们来探讨一下使用GraphQL的内置模式和操作指令,所有符合GraphQL规范的API都必须实现。如果你正在使用一个动态的前端,它们是非常有用的,因为你可以根据用户互动的内容,控制减少响应的有效载荷。

指令的概述

让我们想象一个应用程序,你可以选择自定义表格中显示的列。如果你隐藏了两列或三列,那么就真的不需要为这些单元格获取数据了。不过,通过GraphQL指令,我们可以选择包括或跳过这些字段。

GraphQL规范定义了什么是指令,以及可以使用这些指令的位置。具体来说,指令可以被消费者操作(如查询)使用,也可以被底层模式本身使用。或者,简单地说,指令要么基于模式,要么基于操作。 模式指令在模式生成时使用,而在查询执行时运行。 操作指令当查询被执行时运行。

简而言之,指令可用于元数据、运行时提示、运行时解析(如以特定格式返回日期)和扩展描述(如已废弃)的目的。

四种指令

GraphQL拥有在规范工作草案中定义的四种主要指令,其中有一种指令作为工作草案尚未发布。

  • @include
  • @skip
  • @deprecated
  • @specifiedBy (工作草案)

如果你密切关注GraphQL,你还会注意到有两个额外的指令被合并到JavaScript实现中,你今天可以尝试一下 -@stream@defer 。这些还不是官方规范的一部分,因为社区正在对它们进行实际应用的测试。

@include

@include 指令,正如它的名字一样,允许我们通过传递一个if 参数来有条件地包含字段。由于它是有条件的,在查询中使用一个变量来检查真实性是有意义的。

例如,如果下面的例子中的变量是真实的,那么name 字段将被包括在查询响应中。

query getUsers($showName: Boolean) {
  users {
    id
    name @include(if: $showName)
  }
}

反之,我们可以选择不包括该字段,将变量$showName 作为false 与查询一起传递。我们还可以为$showName 变量指定一个默认值,这样就不需要在每个请求中传递它。

query getUsers($showName: Boolean = true) {
  users {
    id
    name @include(if: $showName)
  }
}

@skip

我们可以用just did来表达同样的东西,但用@skip 指令来代替。如果该值是Truthy,那么正如你所期望的,它将跳过该字段。

query getUsers($hideName: Boolean) {
  users {
    id
    name @skip(if: $hideName)
  }
}

虽然这对单个字段很有效,但有些时候我们可能想包括或跳过一个以上的字段。我们可以像这样在多行重复使用@include@skip

query getUsers($includeFields: Boolean) {
  users {
    id
    name @include(if: $includeFields)
    email @include(if: $includeFields)
    role @include(if: $includeFields)
  }
}

@skip@include 指令都可以用在字段、片段传播和内联片段上,这意味着我们可以做一些其他的事情,像这样代替内联片段。

query getUsers($excludeFields: Boolean) {
  users {
    id
    ... on User @skip(if: $excludeFields) {
      name
      email
      role
    }
  }
}

如果一个片段已经被定义,当我们把一个片段传播到查询中时,我们也可以使用@skip@include

fragment User on User {
  name
  email
  role
}

query getUsers($excludeFields: Boolean) {
  users {
    id
    ...User @skip(if: $excludeFields)
  }
}

@deprecated

@deprecated 指令只出现在模式中,并不是像我们上面看到的那样,用户会作为查询的一部分提供。相反,@deprecated 指令是由维护GraphQL API模式的开发者指定的。

作为一个用户,如果我们试图获取一个在模式中已被废弃的字段,我们会收到一个类似这样的警告,提供上下文帮助。

A zoomed in example of a syntax-highlighted GraphQL query for getUsers, which contains a users object with id and title properties. The title property is underlined in yellow with a contextual tooltip open below it showing a warning in yellow and white that suggests using the name field instead.

在这个例子中,标题字段已被标记为废弃,该指令提供了一个有用的提示来替换它。

为了标记一个被废弃的字段,我们需要在模式定义语言(SDL)中使用@deprecated 指令,在参数中传递一个reason ,像这样。

type User {
  id: ID!
  title: String @deprecated(reason: "Use name instead")
  name: String!
  email: String!
  role: Role
}

如果我们将其与@include 指令配对,我们就可以根据查询变量有条件地获取被废弃的字段。

fragment User on User {
  title @include(if: $includeDeprecatedFields)
  name
  email
  role
}

query getUsers($includeDeprecatedFields: Boolean! = false) {
  users {
    id
    ...User
  }
}

@specifiedBy

@specifiedBy 是指令中的第四条,目前是工作草案的一部分。它被设定为由自定义标量实现使用,并接受一个 参数,该参数应指向标量的规范。url

例如,如果我们为电子邮件地址添加了一个自定义标量,我们将希望把URL传递给作为该标量一部分的重码规范。使用最后一个例子和RFC #822中定义的建议,EmailAddress 的标量将被这样定义在模式中。

scalar EmailAddress @specifiedBy(url: "https://www.w3.org/Protocols/rfc822/")

建议自定义指令有一个前缀的名字,以防止与其他添加的指令发生冲突。如果你正在寻找一个自定义指令的例子,以及它是如何创建的,请看GraphQL公共模式。这是一个自定义的GraphQL指令,它同时支持代码和模式优先,以注释API的哪些部分可以在公共场合消费。

总结

这就是对GraphQL指令的一个高层次审视。同样,我相信指令是一种无名英雄,被其他GraphQL功能所掩盖。我们已经对GraphQL模式有了很多的控制,而指令给了我们更多的细粒度控制,以获得我们想要的查询结果。这就是效率,它使GraphQL API如此快速,并最终对工作更加友好。

如果你正在构建一个GraphQL API,那么一定要把这些指令纳入自省查询。有了这些指令,不仅给开发者带来了额外控制的好处,而且给开发者带来了更好的整体体验。试想一下,正确地将字段@deprecate ,让开发者不需要离开代码就知道该怎么做,这将是多么有帮助?这本身就很强大。