字段与指令的版本控制策略
请先阅读指南 通过字段版本控制演进 Schema,该指南介绍了 Gato GraphQL 中的「字段版本控制」功能。
Gato GraphQL 允许字段和指令接收参数 versionConstraint,以选择使用字段/指令的特定版本(即实现):
query GetPosts {
posts(versionConstraint: "^1.0") {
id
title(versionConstraint: ">=2.1")
excerpt @strUpperCase(versionConstraint: "~1.5.3")
}
}当我们不指定 versionConstraint 参数时会发生什么?例如,下面 Query 中的字段 surname 应该解析到哪个版本?
query GetSurname {
account(id: 1) {
# 应该使用哪个版本?1.0.0?2.0.0?
surname
}
}这里有两个问题:
- 确定在未提供版本时使用的默认版本
- 告知客户端存在多个可供选择的版本
在解决这些问题之前,我们需要了解 GraphQL 在执行 Query 时能提供多少上下文反馈。
执行 Query 时提供上下文反馈
我们需要指出当前 GraphQL 的一个不足之处:它在执行 Query 时没有提供良好的上下文信息。这在废弃(deprecation)方面尤为明显——废弃数据只能通过 Field 和 Enum 类型上的 isDeprecated 和 deprecationReason 字段进行内省查询来获取:
{
__type(name: "Account") {
name
fields {
name
isDeprecated
deprecationReason
}
}
}响应将是:
{
"data": {
"__type": {
"name": "Account",
"fields": [
{
"name": "id",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "surname",
"isDeprecated": true,
"deprecationReason": "Use `personSurname`"
},
{
"name": "personSurname",
"isDeprecated": false,
"deprecationReason": null
}
]
}
}
}然而,当执行包含已废弃字段的 Query 时……
query GetSurname {
account(id: 1) {
surname
}
}……废弃信息不会出现在响应中:
{
"data": {
"account": {
"surname": "Owens"
}
}
}这意味着执行 Query 的开发者必须主动执行内省 Query,才能发现 Schema 是否已升级以及是否有字段被废弃。这种情况可能……偶尔发生?很可能从未发生?
如果 GraphQL API 在执行涉及已废弃字段的 Query 时能提供废弃信息,将大大有助于修订过时的 Query。理想情况下,这些信息可以放在新的顶级条目 deprecations 下,位于 errors 之后、data 之前(遵循规范对响应格式的建议)。
由于 deprecations 顶级条目不是规范的一部分,Gato GraphQL 的「Proactive Feedback」功能通过使用通配符顶级条目 extensions 来支持在 Query 响应中提供更好的反馈,extensions 允许按需扩展协议:

通过 warnings 公告版本信息
我们刚刚了解到 GraphQL 服务器可以使用顶级条目 extensions 来提供废弃信息。我们可以使用同样的方法添加 warnings 条目,以通知开发者某个字段已被版本化。我们不会总是提供此信息;仅当 Query 涉及已版本化的字段且 versionConstraint 参数缺失时才提供。
定义字段的默认版本
可以采用以下几种方法:
- 将
versionConstraint设为必填项 - 在某个日期前默认使用旧版本,该日期之后新版本成为默认版本
- 默认使用最新版本,并鼓励 Query 开发者明确指定所使用的版本
让我们逐一探讨这些策略,并查看执行以下 Query 时的响应:
query GetSurname {
account(id: 1) {
surname
}
}1. 将 versionConstraint 设为必填项
这是最直接的方式:通过将字段参数设为必填项,禁止客户端不指定版本约束。当未提供时,Query 将返回错误。
执行 Query 将响应:
{
"errors": [
{
"message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
}
],
"data": {
"account": {
"surname": null
}
}
}2. 在某个日期前默认使用旧版本,该日期之后新版本成为默认版本
继续使用旧版本,直到某个特定日期,届时新版本将成为默认版本。在此过渡期间,通过 Query 中新增的 extensions.warnings 条目,要求 Query 开发者在该日期之前为旧版本明确添加版本约束。
执行 Query 可能响应:
{
"extensions": {
"warnings": [
{
"message": "Field 'surname' has a new version: '2.0.0'. This version will become the default one on January 1st. We advise you to use this new version already and test that it works fine; if you find any problem, please report the issue in https://github.com/mycompany/myproject/issues. To do the switch, please add the 'versionConstraint' field argument to your query (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints): surname(versionConstraint:\"^2.0\"). If you are unable to switch to the new version, please make sure to explicitly point to the current version '1.0.0' before January 1st: surname(versionConstraint:\"^1.0\"). In case of doubt, please contact us at name@company.com.",
]
},
"data": {
"account": {
"surname": "Owens"
}
}
}3. 使用最新版本,并鼓励用户明确指定所使用的版本
当 versionConstraint 未设置时,使用字段的最新版本,并通过新的 extensions.warnings 条目显示该字段所有可用版本的列表,鼓励 Query 开发者明确定义要使用的版本:
执行 Query 可能响应:
{
"extensions": {
"warnings": [
{
"message": "Field 'surname' has more than 1 version. Please add the 'versionConstraint' field argument to your query to indicate which version to use (using Composer's semver constraint rules; see https://getcomposer.org/doc/articles/versions.md#writing-version-constraints). To use the latest version, use: surname(versionConstraint:\"^2.0\"). Available versions: '2.0.0', '1.0.0'.",
]
},
"data": {
"account": {
"surname": "Owens"
}
}
}指令的版本控制
我们可以使用相同的策略对指令进行版本控制。例如,在不提供版本约束的情况下执行 Query:
query {
post(by: { id: 1 }) {
title @strTitleCase
}
}它可以假设使用某个默认版本,并为开发者生成一条警告消息以提示修订 Query:
