GraphQL 概論
Find a file
2022-01-18 16:48:47 +09:00
.github/workflows use .marprc.yml 2021-07-20 00:03:28 +09:00
.marprc.yml use .marprc.yml 2021-07-20 00:03:28 +09:00
LICENSE Create LICENSE 2021-07-06 07:02:36 +09:00
README.md fix 2022-01-18 16:48:47 +09:00

marp paginate
true true

GraphQL 概論

WebDINO Japan エンジニア 渡邉浩平 w:200


はじめに

内容

  • GraphQL とは
  • なぜ GraphQL を使うのか
  • GraphQL Query ハンズオン

GraphQL の基礎を学び、実際に GraphQL API からデータを取得してみる


GraphQL とは

https://graphql.org


GraphQL とは API の問い合わせ言語

サーバーへの問い合わせ (GraphQL Query)

query {
  pokemon(name: "Pikachu") {
    classification
  }
}

サーバーからの応答 (JSON)

{
  "data": {
    "pokemon": {
      "classification": "Mouse Pokémon"
    }
  }
}

https://graphql-pokemon2.vercel.app


歴史

  • 2012 年 Facebook による開発
  • 2015 年 オープンソース化
  • 2019 年 GraphQL Foundationに移管

オープンソースな仕様になっており、自由に貢献できる


仕様

サーバーとクライアントの間のやり取りに使われる言語仕様 https://spec.graphql.org

問い合わせ言語 - GraphQL Query

クライアントがサーバーに JSON のデータを問い合わせるための言語 https://graphql.org/learn/queries/

身近な問い合わせ言語の例: SQL

スキーマ言語 - GraphQL Schema

データ構造と操作を宣言するための言語 https://graphql.org/learn/schema/

身近なスキーマ言語の例: JSON Schema, XML Schema


何でないか

  • データベースではない
  • JavaScript ではない

なぜ GraphQL を使うのか

  1. 単一リクエスト
  2. 型システム
  3. 便利なツール

1. 単一リクエスト

GraphQL は単一のリクエストで指定したデータを取得できる

  • クライアントのデータ取得の効率化
  • エンドポイントの管理の容易さ

Facebook が GraphQL を開発した理由は、モバイルネイティブアプリ対応のため スマホの普及に伴う低速、省電力なデバイスの利用の増加が背景


REST

h:600 REST https://hasura.io/learn/graphql/intro-graphql/graphql-vs-rest/


GraphQL

h:600 GraphQL https://hasura.io/learn/graphql/intro-graphql/graphql-vs-rest/


REST vs GraphQL

特徴 REST GraphQL
オーバーフェッチの解消
アンダーフェッチの解消
エンドポイントの管理の容易さ
エンドポイントの実装の単純さ

GraphQL は複数のリソースを単一の HTTP POST (Query のみなら GET) リクエストで取得できる その代わり、エンドポイントの実装は REST と比較して複雑になりやすい


2. 型システム

単一リクエストを支える GraphQL エンドポイントの構築と正確なデータ構造の維持 クライアントアプリケーションを実行するフロントエンドフレームワークとプラットフォームの多様化が背景


GraphQL Schema

データ構造と操作を宣言するスキーマ言語

"""ポケモンを表します"""
type Pokemon {
  """このオブジェクトのID"""
  id: ID!

  """このポケモンの名前"""
  name: String

  # ...
}

"""ポケモンの寸法を表します"""
type PokemonDimension {
  # ...
}

オブジェクトの種類とその構造を宣言できる


特定のプログラミング言語に依存しない

// JavaScript
const pokemonQuery = `{ pokemon(name: "Pikachu") { classification } }`;

fetch(`http://example/?${new URLSearchParams({ query: pokemonQuery })}`)
  .then((r) => r.json())
  .then(({ data }) => console.log(data?.pokemon?.classification));
// => "Mouse Pokémon"
// Kotlin
val response = apolloClient.query(pokemonQuery).await()
Log.d(response?.data?.pokemon?.classification)
// Swift
apollo.fetch(query: pokemonQuery) { result in
  guard let data = try? result.get().data else { return }
  print(data.pokemon?.classification)
}

3. 便利なツール

短期間での開発

クライアントアプリケーションの設計変更に対応するためのツールが提供されている 継続的デプロイ (CD) が背景


代表的なツールの紹介

  • GraphiQL … 一般的な IDE
  • GraphQL Playground … より強力な IDE
  • Public GraphQL APIs … 公開されている GraphQL API の一覧
  • GraphQL Code Generator … 自動コード生成
  • Hasura … GraphQL サーバー

GraphiQL

h:600 GraphiQL


GraphQL Playground

h:600 GraphQL Playground


Public GraphQL APIs

公開されている GraphQL API 一覧の紹介 GraphQL がどういうものか実際に試してみるのに便利

たとえば

https://apis.guru/graphql-apis/


GraphQL Code Generator

コードの生成

$ graphql-codegen

使う

import { usePokemonQuery } from "./generated";

export default () => {
  const { data } = usePokemonQuery();
  return data?.pokemon?.classification;
};

React, Vue, Kotlin, etc.

https://www.graphql-code-generator.com


Hasura

GraphQL サーバー 接続したデータベースを自動的に GraphQL API として提供

https://hasura.io


ここまでのまとめ

GraphQL の基礎知識

  • GraphQL とは API の問い合わせ言語仕様
  • 特徴
    • 単一リクエスト
    • 型システム
    • 便利なツール

GraphQL Query ハンズオン


GraphQL Query ハンズオン

内容

  • GraphQL の操作
  • query 操作によるデータの取得

実際に GraphQL API からデータを取得できるようになる


GraphQL Operation

3 種類の操作

  • query - 読み取り
  • mutation - 書き込み
  • subscription - イベントストリーム

1 つのリクエストに複数の操作を含めることができる


Query - データの取得


ゲットだぜ!

実際に Pokémon API (非公式) を使ってデータを取得してみる

https://graphql-pokemon2.vercel.app/?query=query%20%7B%0A%20%20pokemons(first%3A%20151)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D%0A

この URL にアクセス

または

  1. https://graphql-pokemon2.vercel.app にアクセス
  2. 下記の Query を入力 > 実行 (▶) を選択
query {
  pokemons(first: 151) {
    name
  }
}

取得結果

{
  "data": {
    "pokemons": [
      {
        "name": "Bulbasaur"
      },
      {
        "name": "Ivysaur"
      },
      {
        "name": "Venusaur"
      },
      {
        // ...
      }
    ]
  }
}

ポケモンに関する情報を JSON で取得できた


基本的な構文

query {
  pokemons(first: 151) {
    name
  }
}

query … 操作 pokemons, name … フィールド first… 引数 151 … 値


別の取得例

https://graphql-pokemon2.vercel.app/?query=query%20%7B%0A%20%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%0A%20%20%7D%0A%7D%0A

この URL にアクセス

または

  1. https://graphql-pokemon2.vercel.app にアクセス
  2. 下記の Query を入力 > 実行 (▶) を選択
query {
  pokemon(name: "Pikachu") {
    classification
  }
}

取得結果

{
  "data": {
    "pokemon": {
      "classification": "Mouse Pokémon"
    }
  }
}

ポケモンに関する情報を JSON で取得できた


基本的な構文

query {
  pokemon(name: "Pikachu") {
    classification
  }
}

query … 操作 pokemon, classification … フィールド name … 引数 "Pikachu" … 値


子孫関係の取得例

https://graphql-pokemon2.vercel.app/?query=query%20%7B%0A%20%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%0A%20%20%20%20height%20%7B%0A%20%20%20%20%20%20minimum%0A%20%20%20%20%20%20maximum%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D

この URL にアクセス

または

query {
  pokemon(name: "Pikachu") {
    classification
    height {
      minimum # <=
      maximum # <= これらのフィールドが加わる
    }
  }
}

子孫関係

フィールドにフィールドを追加することで子孫関係を取得できる

query {
  pokemon(name: "Pikachu") {
    classification
    height {
      minimum
      maximum
    }
  }
}

height フィールドの中の minimum, maximum フィールド


変数の使用例

https://graphql-pokemon2.vercel.app/?query=query%20(%24name%3A%20String!)%20%7B%0A%20%20pokemon(name%3A%20%24name)%20%7B%0A%20%20%20%20classification%0A%20%20%20%20height%20%7B%0A%20%20%20%20%20%20minimum%0A%20%20%20%20%20%20maximum%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&variables=%7B%0A%20%20%22name%22%3A%20%22Pikachu%22%0A%7D

この URL にアクセス


変数

変数を使うことで Query を再利用できる

query ($name: String!) { # <= 変数の定義
  pokemon(name: $name) { # <= 変数の使用
    classification
    height {
      minimum
      maximum
    }
  }
}

$name … 変数 (例えば { "name": "Pikachu" } によって代入) String! … 型


操作名の使用例

https://graphql-pokemon2.vercel.app/?query=query%20fetchPokemonNames%20%7B%0A%20%20pokemons(first%3A%20151)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D%0A%0Aquery%20fetchPikachu%20%7B%0A%20%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%0A%20%20%7D%0A%7D&operationName=fetchPokemonNames

この URL にアクセス

query fetchPokemonNames { # <= 操作に名前を付ける
  pokemons(first: 151) {
    name
  }
}

query fetchPikachu {      # <= 操作に名前を付ける
  pokemon(name: "Pikachu") {
    classification
  }
}

操作名

操作に名前を付けることで複数の操作を識別できる

query fetchPokemonNames {
  pokemons(first: 151) {
    name
  }
}

query fetchPikachu {
  pokemon(name: "Pikachu") {
    classification
  }
}

fetchPokemonNames, fetchPikachu … 操作名


エイリアスの使用例

https://graphql-pokemon2.vercel.app/?query=query%20%7B%0A%20%20pikachu%3A%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%0A%20%20%7D%0A%7D&variables=

この URL にアクセス

または

query {
  pikachu: pokemon(name: "Pikachu") { # <= フィールドに名前を付ける
    classification
  }
}

取得結果

{
  "data": {
    "pikachu": { // <= 名付けたプロパティで取得できる
      "classification": "Mouse Pokémon"
    }
  }
}

エイリアス

フィールドに名前を付ける

query {
  pikachu: pokemon(name: "Pikachu") {
    classification
  }
}

pikachu … エイリアス


フラグメントの使用例

https://graphql-pokemon2.vercel.app/?query=fragment%20dimension%20on%20PokemonDimension%20%7B%0A%20%20minimum%0A%20%20maximum%0A%7D%0A%0Aquery%20%7B%0A%20%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%0A%20%20%20%20height%20%7B%0A%20%20%20%20%20%20...dimension%0A%20%20%20%20%7D%0A%20%20%20%20weight%20%7B%0A%20%20%20%20%20%20...dimension%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D

この URL にアクセス

または

fragment dimension on PokemonDimension {
  minimum
  maximum
}

query {
  pokemon(name: "Pikachu") {
    classification
    height {
      ...dimension
    }
    weight {
      ...dimension
    }
  }
}

フラグメント

いくつかのフィールドをまとめ、そのフィールドを取得する際に使用できる

fragment dimension on PokemonDimension {
  minimum
  maximum
}

query {
  pokemon(name: "Pikachu") {
    classification
    height {
      ...dimension
    }
    weight {
      ...dimension
    }
  }
}

dimension … フラグメント


ディレクティブの使用例

https://graphql-pokemon2.vercel.app/?query=query%20(%24showClassification%3A%20Boolean!)%20%7B%0A%20%20pokemon(name%3A%20%22Pikachu%22)%20%7B%0A%20%20%20%20classification%20%40include(if%3A%20%24showClassification)%0A%20%20%7D%0A%7D%0A&variables=%7B%0A%20%20%22showClassification%22%3A%20true%0A%7D

この URL にアクセス


ディレクティブ

query ($showClassification: Boolean!) {
  pokemon(name: "Pikachu") {
    classification @include(if: $showClassification)
  }
}

変数

{
  "showClassification": true
}

@include … ディレクティブ


まとめ

  • 3 種類の操作
  • Query によるデータの取得

フィードバック

このスライドを編集する / 問題を報告する


後付


より理解を深めるための知識


セキュリティ

セキュリティの懸念事項は一般的な Web サービスと同様に存在


認証・認可

GraphQL 仕様に含まないので一般的な Web の認証・認可の設計と同様に行う


キャッシュ

GraphQL にはグローバルなオブジェクトの識別子の宣言によるキャッシュ機構がある

もし HTTP GET メソッドを使用する場合、HTTP キャッシュを利用できる


JSON Serialization

GraphQL Value JSON Value
Map Object
List Array
Null null
String/Enum Value String
Boolean true or false
Int/Float Number

https://spec.graphql.org/June2018/#sec-JSON-Serialization