はじめに
GraphQLをざっくり理解するためにやったことをまとめときます。
GraphQLとはなんなのか的なことは、こちらの記事に丸投げします。肩がねじ切れんばかりに投げてますが、自分はクエリ言語(フロント側、リクエスト)とスキーマ言語(サーバー側、レスポンス)に分けて写経などをしました。
RestAPIだと処理ごとにエンドポイントがありましたが、GraphQLではエンドポイントは1つです。GraphQLは、サーバー側で何かをする関数を用意しておき、その関数をフロント側で実行するイメージで理解しています。
今回はクエリ言語のみまとめます。
クエリ言語
クエリ言語は、GraphQL APIのリクエストのための言語で、これはさらにデータ取得系のquery、データ更新系のmutation、サーバーサイドからのイベントの通知であるsubscriptionの3種類があります。
はい、クエリ言語とはこういうことです。右肩が痛いですね。今回はsubscriptionについては触れませんが、ざっくりと説明するとsubscriptionは、サーバー側の変更をクライアント側でホットリロードするような時に使うみたいです。
query、mutation、paginationをGraphQL Explorer(GithubのGraphQL API)で試していきます。paginationは、サーバーが一度に全てではなく、小さなチャンクに分けてデータを送信できる仕組みです。
こちらの記事を参考にしました。(GraphQLをReactで扱う方法もあり、分かりやすいです。)
query
データの取得をする。(データの読み取り)
基本
// クエリ
query {
viewer{ // GithubのGraphQL API
name
url
}
}
// レスポンス
{
"data": {
"viewer": {
"name": "kirisawa",
"url": "https://github.com/kirikirisu"
}
}
}
・viewerをオブジェクトという。
・nameやurlをフィールドと言い、オブジェクトの特定のプロパティを要求するために使用される。
・クエリは単なるオブジェクトとフィールド、オブジェクトはフィールドとも呼ぶ。
・オブジェクトはサーバ側で"スキーマ"で定義されており、定義されているものをDocから確認できる。(後から出てくるMutationも)
フィールド(オブジェクト)に引数を渡す
// クエリ
query {
organization(login: "the-road-to-learn-react"){
name
url
}
}
// レスポンス
{
"data": {
"organization": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react"
}
}
}
・organizationフィールドに公開されいるorganization名を渡す。
・引数はstringだけでなく、numberやbooleanなどを渡す場合もある。
・フィールドレベルでリクエストを指定できるため、クエリの柔軟性が高い。
2つ以上の同一オブジェクトにデータを要求する
・2つ以上の同一のオブジェクトにデータを要求するには決まったエイリアスを使う。
// クエリ
// これは失敗する
query {
organization(login: "the-road-to-learn-react") {
name
url
}
organization(login: "facebook") {
name
url
}
}
// こうする
query {
book: organization(login: "the-road-to-learn-react") {
name
url
}
company:organization(login: "facebook") {
name
url
}
}
// レスポンス
{
"data": {
"book": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react"
},
"company": {
"name": "Facebook",
"url": "https://github.com/facebook"
}
}
}
フィールドの共通化
・フラグメントを使用し、フィールドの再利用できる部分を抽出する。
// クエリ
query {
book: organization(login: "the-road-to-learn-react") {
...sharedOrganizationFields
}
company: organization(login: "facebook") {
...sharedOrganizationFields
}
}
fragment sharedOrganizationFields on Organization {
name
url
}
// レスポンス
{
"data": {
"book": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react"
},
"company": {
"name": "Facebook",
"url": "https://github.com/facebook"
}
}
}
・「on Organization」は、GithubのGraphQL APIによって定義されたオブジェクトのタイプ。TypeScriptでの型定義のようなもの。
変数を使う
・"Query Variables"から変数を提供する。
・変数を使うことで動的にクエリを作ることができる。
・$ 記号で引数を変数として定義する。
・ ! はその引数が必須であることを示す。ない場合は必須ではない。
// クエリ
query ($organization: String!) {
organization(login: $organization) {
name
url
}
}
// Query Variables
{
"organization": "the-road-to-learn-react"
}
// レスポンス
{
"data": {
"organization": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react"
}
}
}
クエリに名前をつける
・JavaScript無名関数と名前付き関数に似ている。
・付けた名前は、「オペレーションタイプ」や「オペレーションネーム」と言われる。
// 名前なし
query {
organization(login: "the-road-to-learn-react") {
name
url
}
}
// 名前あり
query OrganizationForLearningReact {
organization(login: "the-road-to-learn-react") {
name
url
}
}
// 名前あり、引数あり
query OrganizationForLearningReact($organization: String!) {
organization(login: $organization) {
name
url
}
}
// レスポンスは同じ
ネストされたオブジェクトにアクセスする
・ネストされたオブジェクトにアクセスするためには、クエリもネストするだけ。
// クエリ
query OrganizationForLearningReact(
$organization: String!,
$repository: String!
) {
organization(login: $organization) {
name
url
repository(name: $repository) {
name
}
}
}
// Query Variables
{
"organization": "the-road-to-learn-react",
"repository": "the-road-to-learn-react-chinese"
}
ディレクティブ
・@include,@skipによって特定のフィールドを取得したり、取得しなかったりできる。
・@includeは、ifで評価する変数が true の時、フィールドを取得する。(falseの時取得しない)
・@skipは、falseの時フィールドを取得する。(trueの時取得しない)
// クエリ
query OrganizationForLearningReact(
$organization: String!,
$repository: String!,
$withFork: Boolean!
) {
organization(login: $organization) {
name
url
repository(name: $repository) {
name
forkCount @include(if: $withFork)
}
}
}
// Query Variables
{
"organization": "the-road-to-learn-react",
"repository": "the-road-to-learn-react-chinese",
"withFork": true
}
mutation
GraphQL APIから提供され、何かを実行する。(データの書き込み)
例
・Githubのリポジトリにスターをつけるmutation。
// まずクエリによってリポジトリのidを取得する
query {
organization(login: "the-road-to-learn-react") {
name
url
repository(name: "the-road-to-learn-react") {
id
name
}
}
}
// レスポンス
{
"data": {
"organization": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react",
"repository": {
"id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
"name": "the-road-to-learn-react"
}
}
}
}
// そしてミューテーションによってリポジトリにスターをつける
mutation AddStar ($repositoryId: ID!) {
addStar(input: { starrableId: $repositoryId }){
starrable {
id
viewerHasStarred
}
}
}
// Query Variables
{
"repositoryId": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw=="
}
// レスポンス
{
"data": {
"addStar": {
"starrable": {
"id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
"viewerHasStarred": true
}
}
}
}
・mutation(addStar)は、Github GraphQL APIから提供されている。
・上記は名前(AddStar)付きミューテーションで、名前は任意の名前をつける。
・クエリのようにオブジェクトとフィールドを使用して、ミューテーションの戻り値を指定できる。
// スターを消すミューテーション
mutation AddStar($repositoryId: ID!) {
removeStar(input: { starrableId: $repositoryId }) {
starrable {
id
viewerHasStarred
}
}
}
// レスポンス
{
"data": {
"removeStar": {
"starrable": {
"id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
"viewerHasStarred": false
}
}
}
}
Pagination
大きなデータを複数に分けて取得する
例
リポジトリリストの中からふたつずつリポジトリを取得
query OrganizationForLearningReact {
organization(login: "the-road-to-learn-react") {
name
url
repositories(first: 2) {
edges {
node {
name
}
cursor
}
}
}
}
// レスポンス
{
"data": {
"organization": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react",
"repositories": {
"edges": [
{
"node": {
"name": "the-road-to-learn-react"
},
"cursor": "Y3Vyc29yOnYyOpHOA8awSw=="
},
{
"node": {
"name": "hackernews-client"
},
"cursor": "Y3Vyc29yOnYyOpHOBGhimw=="
}
]
}
}
}
}
・ (first: 2)でリポジトリスト上から2つを指定。cursorは、そのリポジトリの位置情報。
・次に、取得した位置情報から2つのリポジトリを取得する。
query OrganizationForLearningReact {
organization(login: "the-road-to-learn-react") {
name
url
repositories(first: 2, after: "Y3Vyc29yOnYyOpHOA8awSw==") {
edges {
node {
name
}
cursor
}
}
}
}
// レスポンス
{
"data": {
"organization": {
"name": "The Road to learn React",
"url": "https://github.com/the-road-to-learn-react",
"repositories": {
"edges": [
{
"node": {
"name": "hackernews-client"
},
"cursor": "Y3Vyc29yOnYyOpHOBGhimw=="
},
{
"node": {
"name": "react-local-storage"
},
"cursor": "Y3Vyc29yOnYyOpHOBUuT2A=="
}
]
}
}
}
}
最後に
最後まで読んでいただきありがとうございました。