コンセプト、アイデア、戦略
コンセプト、アイデア、戦略フィールドとディレクティブのバージョニング戦略

フィールドとディレクティブのバージョニング戦略

まず、Gato GraphQL の「フィールドバージョニング」機能を説明したガイド フィールドバージョニングによるスキーマの進化 をお読みください

Gato GraphQL では、フィールドとディレクティブが引数 versionConstraint を受け取ることができ、使用するフィールド/ディレクティブの特定バージョン(実装)を選択できます:

query GetPosts {
  posts(versionConstraint: "^1.0") {
    id
    title(versionConstraint: ">=2.1")
    excerpt @strUpperCase(versionConstraint: "~1.5.3")
  }
}

versionConstraint 引数を指定しない場合はどうなるでしょうか?たとえば、以下のクエリにおけるフィールド surname はどのバージョンに解決されるべきでしょうか?

query GetSurname {
  account(id: 1) {
    # どのバージョンを使用すべきか?1.0.0?2.0.0?
    surname
  }
}

ここでは 2 つの課題があります:

  1. バージョンが指定されない場合に使用するデフォルトバージョンを決定する
  2. クライアントに複数のバージョンから選択できることを通知する

これらの課題に取り組む前に、GraphQL がクエリ実行時にどの程度コンテキスト情報を提供できるかを確認する必要があります。

クエリ実行時のコンテキスト情報の提供

現在の GraphQL には理想的ではない点があることを指摘しておく必要があります:クエリ実行時に十分なコンテキスト情報を提供していません。これは非推奨(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 GetSurname {
  account(id: 1) {
    surname
  }
}

…非推奨情報はレスポンスに表示されません:

{
  "data": {
    "account": {
      "surname": "Owens"
    }
  }
}

これは、クエリを実行する開発者がスキーマが更新されたかどうか、またいずれかのフィールドが非推奨になったかどうかを確認するために、積極的にイントロスペクションクエリを実行しなければならないことを意味します。それが起こるのは……たまに?おそらくほぼないでしょう?

非推奨フィールドを含むクエリを実行する際に GraphQL API が非推奨情報を提供すれば、古いクエリの見直しに向けた大きな改善となるでしょう。この情報は理想的には、新しいトップレベルエントリ deprecations の下に表示され、errors の後、data の前(仕様のレスポンス形式の提案に従って)に配置されるべきです。

deprecations トップレベルエントリは仕様の一部ではないため、Gato GraphQL の「Proactive Feedback」機能は、プロトコルを必要に応じて拡張できるワイルドカードトップレベルエントリ extensions を使用して、クエリへのレスポンスでより良いフィードバックをサポートします:

クエリレスポンスにおける非推奨情報

warnings によるバージョンの公知

GraphQL サーバーがトップレベルエントリ extensions を使用して非推奨情報を提供できることを学びました。この同じ手法を使って warnings エントリを追加し、フィールドがバージョニングされていることを開発者に通知することができます。この情報は常に提供するわけではなく、クエリがバージョニングされたフィールドを含み、かつ versionConstraint 引数が指定されていない場合にのみ提供します。

フィールドのデフォルトバージョンの定義

使用できるアプローチはいくつかあります:

  1. versionConstraint を必須にする
  2. 特定の日付まで旧バージョンをデフォルトとして使用し、その日付から新バージョンがデフォルトになる
  3. 最新バージョンをデフォルトとして使用し、クエリ開発者に使用するバージョンを明示的に指定するよう促す

各戦略を検討し、次のクエリを実行した際のレスポンスを確認しましょう:

query GetSurname {
  account(id: 1) {
    surname
  }
}

1. versionConstraint を必須にする

最もわかりやすいアプローチです:フィールド引数を必須にすることで、クライアントがバージョン制約を指定しないことを禁止します。指定されない場合、クエリはエラーを返します。

クエリを実行すると次のレスポンスが返されます:

{
  "errors": [
    {
      "message": "Argument 'versionConstraint' in field 'surname' cannot be empty"
    }
  ],
  "data": {
    "account": {
      "surname": null
    }
  }
}

2. 特定の日付まで旧バージョンをデフォルトとして使用し、その日付から新バージョンがデフォルトになる

特定の日付まで旧バージョンを使用し続け、その日付から新バージョンがデフォルトになります。この移行期間中は、クエリの新しい extensions.warnings エントリを通じて、その日付より前に旧バージョンへのバージョン制約を明示的に追加するよう、クエリ開発者に依頼します。

クエリを実行すると次のようなレスポンスが返されます:

{
  "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 エントリを通じてそのフィールドで利用可能なすべてのバージョンの一覧を表示しながら、クエリ開発者に使用するバージョンを明示的に定義するよう促します:

クエリを実行すると次のようなレスポンスが返されます:

{
  "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 {
  post(by: { id: 1 }) {
    title @strTitleCase
  }
}

デフォルトのバージョンを仮定して使用し、開発者がクエリを見直すための警告メッセージを生成できます:

バージョン制約なしでバージョニングされたディレクティブをクエリする