GraphQLサーバーの仕様
このドキュメントの目的は、RelayがGraphQLサーバーに対して行う前提条件を明示し、GraphQLスキーマの例を通じてそれらを示すことです。
目次
まえがき
RelayがGraphQLサーバーに対して行う2つの主要な前提条件は、以下のものを提供することです。
- オブジェクトを再取得するためのメカニズム。
- コネクションをページングする方法の説明。
この例では、これら2つの前提条件をすべて示します。この例は包括的ではありませんが、これらの主要な前提条件を迅速に紹介し、ライブラリの詳細な仕様に入る前にいくつかのコンテキストを提供することを目的としています。
この例の前提は、GraphQLを使用して、オリジナルのスターウォーズ三部作における船と勢力に関する情報をクエリしたいということです。
読者はすでにGraphQLに精通していることを前提としています。そうでない場合は、GraphQL.jsのREADMEから始めるのが良いでしょう。
また、読者はすでにスターウォーズに精通していることも前提としています。そうでない場合は、1977年版のスターウォーズから始めるのが良いでしょう。ただし、このドキュメントの目的には1997年特別編でも十分です。
スキーマ
以下に示すスキーマは、Relayで使用されるGraphQLサーバーが実装する必要がある機能を実証するために使用されます。2つの主要な型は、スターウォーズの世界における勢力と船であり、勢力には多数の船が関連付けられています。
interface Node {
  id: ID!
}
type Faction implements Node {
  id: ID!
  name: String
  ships: ShipConnection
}
type Ship implements Node {
  id: ID!
  name: String
}
type ShipConnection {
  edges: [ShipEdge]
  pageInfo: PageInfo!
}
type ShipEdge {
  cursor: String!
  node: Ship
}
type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}
type Query {
  rebels: Faction
  empire: Faction
  node(id: ID!): Node
}
オブジェクトの識別
FactionとShipの両方に、再取得に使用できる識別子があります。この機能をRelayに公開するには、Nodeインターフェースとルートクエリ型のnodeフィールドを使用します。
Nodeインターフェースには、ID!である単一のフィールドidが含まれています。nodeルートフィールドは、単一の引数であるID!を取り、Nodeを返します。これら2つは連携して再取得を可能にします。そのフィールドで返されたidをnodeフィールドに渡すと、オブジェクトが返されます。
実際にこれを見て、反乱軍のIDをクエリしてみましょう
query RebelsQuery {
  rebels {
    id
    name
  }
}
戻り値
{
  "rebels": {
    "id": "RmFjdGlvbjox",
    "name": "Alliance to Restore the Republic"
  }
}
これで、システム内の反乱軍のIDがわかりました。これで、それらを再取得できます
query RebelsRefetchQuery {
  node(id: "RmFjdGlvbjox") {
    id
    ... on Faction {
      name
    }
  }
}
戻り値
{
  "node": {
    "id": "RmFjdGlvbjox",
    "name": "Alliance to Restore the Republic"
  }
}
帝国についても同じことを行うと、異なるIDが返され、同様に再取得できることがわかります
query EmpireQuery {
  empire {
    id
    name
  }
}
結果
{
  "empire": {
    "id": "RmFjdGlvbjoy",
    "name": "Galactic Empire"
  }
}
そして
query EmpireRefetchQuery {
  node(id: "RmFjdGlvbjoy") {
    id
    ... on Faction {
      name
    }
  }
}
結果
{
  "node": {
    "id": "RmFjdGlvbjoy",
    "name": "Galactic Empire"
  }
}
Nodeインターフェースとnodeフィールドは、この再取得のためにグローバルに一意のIDを想定しています。グローバルに一意のIDがないシステムでは、通常、型を型固有のIDと組み合わせることで合成できます。これはこの例で実行されたことです。
返されたIDはbase64文字列でした。IDは不透明になるように設計されています(nodeのid引数に渡す必要があるのは、システム内の一部のオブジェクトでidをクエリした変更されていない結果のみです)。また、文字列をbase64エンコードすることは、その文字列が不透明な識別子であることを表示者に認識させるための便利な慣習です。
サーバーがどのように動作すべきかについての完全な詳細は、GraphQLサイトのGraphQLオブジェクト識別ベストプラクティスガイドで入手できます。
コネクション
勢力には、スターウォーズの世界に多くの船があります。Relayには、これらの1対多の関係を簡単に操作するための機能が含まれており、これらの1対多の関係を表現するための標準化された方法を使用しています。この標準コネクションモデルは、コネクションをスライスおよびページネーションする方法を提供します。
反乱軍を取り上げて、最初の船を要求しましょう
query RebelsShipsQuery {
  rebels {
    name
    ships(first: 1) {
      edges {
        node {
          name
        }
      }
    }
  }
}
結果
{
  "rebels": {
    "name": "Alliance to Restore the Republic",
    "ships": {
      "edges": [
        {
          "node": {
            "name": "X-Wing"
          }
        }
      ]
    }
  }
}
これにより、結果セットを最初の1つにスライスするために、shipsにfirst引数が使用されました。しかし、ページネーションしたい場合はどうでしょうか?各エッジには、ページネーションに使用できるカーソルが公開されます。今回は最初の2つを要求し、カーソルも取得しましょう
query MoreRebelShipsQuery {
  rebels {
    name
    ships(first: 2) {
      edges {
        cursor
        node {
          name
        }
      }
    }
  }
}
そして、以下が返されます
{
  "rebels": {
    "name": "Alliance to Restore the Republic",
    "ships": {
      "edges": [
        {
          "cursor": "YXJyYXljb25uZWN0aW9uOjA=",
          "node": {
            "name": "X-Wing"
          }
        },
        {
          "cursor": "YXJyYXljb25uZWN0aW9uOjE=",
          "node": {
            "name": "Y-Wing"
          }
        }
      ]
    }
  }
}
カーソルがbase64文字列であることに注意してください。これは以前のパターンです。サーバーはこれが不透明な文字列であることを思い出させています。この文字列をshipsフィールドのafter引数としてサーバーに渡すことができます。これにより、前の結果の最後の船の後の次の3隻の船を要求できます。
query EndOfRebelShipsQuery {
  rebels {
    name
    ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") {
      edges {
        cursor
        node {
          name
        }
      }
    }
  }
}
以下が得られます
{
  "rebels": {
    "name": "Alliance to Restore the Republic",
    "ships": {
      "edges": [
        {
          "cursor": "YXJyYXljb25uZWN0aW9uOjI=",
          "node": {
            "name": "A-Wing"
          }
        },
        {
          "cursor": "YXJyYXljb25uZWN0aW9uOjM=",
          "node": {
            "name": "Millennium Falcon"
          }
        },
        {
          "cursor": "YXJyYXljb25uZWN0aW9uOjQ=",
          "node": {
            "name": "Home One"
          }
        }
      ]
    }
  }
}
やった!さらに進んで、次の4つを取得しましょう!
query RebelsQuery {
  rebels {
    name
    ships(first: 4 after: "YXJyYXljb25uZWN0aW9uOjQ=") {
      edges {
        cursor
        node {
          name
        }
      }
    }
  }
}
結果
{
  "rebels": {
    "name": "Alliance to Restore the Republic",
    "ships": {
      "edges": []
    }
  }
}
ふむ。もう船はありませんでした。反乱軍のためにシステムに5隻しかなかったようです。それを検証するためにもう一度ラウンドトリップする必要なく、コネクションの終わりに到達したことを知ることができたらよかったでしょう。コネクションモデルは、PageInfoと呼ばれる型でこの機能を提供します。したがって、船を取得した2つのクエリをもう一度発行しますが、今回はhasNextPageを要求します
query EndOfRebelShipsQuery {
  rebels {
    name
    originalShips: ships(first: 2) {
      edges {
        node {
          name
        }
      }
      pageInfo {
        hasNextPage
      }
    }
    moreShips: ships(first: 3 after: "YXJyYXljb25uZWN0aW9uOjE=") {
      edges {
        node {
          name
        }
      }
      pageInfo {
        hasNextPage
      }
    }
  }
}
そして、以下が返されます
{
  "rebels": {
    "name": "Alliance to Restore the Republic",
    "originalShips": {
      "edges": [
        {
          "node": {
            "name": "X-Wing"
          }
        },
        {
          "node": {
            "name": "Y-Wing"
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": true
      }
    },
    "moreShips": {
      "edges": [
        {
          "node": {
            "name": "A-Wing"
          }
        },
        {
          "node": {
            "name": "Millennium Falcon"
          }
        },
        {
          "node": {
            "name": "Home One"
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": false
      }
    }
  }
}
そのため、船に対する最初のクエリで、GraphQLは次のページがあることを知らせましたが、次のクエリでは、コネクションの終わりに到達したことを知らせました。
Relayは、この機能のすべてを使用してコネクションに関する抽象化を構築し、クライアントでカーソルを手動で管理しなくても効率的に操作できるようにします。
サーバーがどのように動作すべきかについての完全な詳細は、GraphQLカーソルコネクション仕様で入手できます。
さらに詳しく
これでGraphQLサーバーの仕様の概要は終わりです。Relay準拠のGraphQLサーバーの詳細な要件については、Relayカーソルコネクションモデル、GraphQLグローバルオブジェクト識別モデルの詳細な説明がすべて利用可能です。
仕様を実装するコードを確認するには、GraphQL.js Relayライブラリで、ノードとコネクションを作成するためのヘルパー関数を提供しています。そのリポジトリの__tests__フォルダーには、リポジトリの統合テストとして上記の例の実装が含まれています。
このページは役に立ちましたか?
いくつかの簡単な質問に答えることで、サイトをさらに改善するのにご協力ください いくつかの簡単な質問に答える.