title | marp | paginate |
---|---|---|
GraphQL 概論 |
true |
true |
WebDINO Japan シニアエンジニア 渡邉浩平
データ層分野
内容
- GraphQL とは
- なぜ GraphQL を使うのか
- GraphQL Query ハンズオン
GraphQL の基礎を学び、実際に GraphQL API からデータを取得してみる
クライアントからサーバーへの問い合わせ (GraphQL Query)
query {
pokemon(name: "Pikachu") {
classification
}
}
結果は JSON で返却
{
"data": {
"pokemon": {
"classification": "Mouse Pokémon"
}
}
}
https://graphql-pokemon2.vercel.app
- 2012 年 Facebook (現 Meta) による開発
- 2015 年 オープンソース化
- 2019 年 GraphQL Foundation に移管
Facebook が GraphQL を開発した理由は、モバイルネイティブアプリ対応のため スマートフォン普及に伴うプラットフォームの多様化が背景
現在はオープンソースな仕様になっており、自由に貢献できる https://github.com/graphql/graphql-spec
クライアントからサーバーに問い合わせるための言語 https://graphql.org/learn/queries/
GraphQL 以外の身近な問い合わせ言語の例: SQL
データ構造と操作を宣言するための言語 https://graphql.org/learn/schema/
- グラフデータベースではない
- JavaScript ではない
- 単一リクエスト
- 型システム
- 便利なツール
GraphQL は単一のリクエストで指定したデータを取得できる
https://hasura.io/learn/graphql/intro-graphql/graphql-vs-rest/
https://hasura.io/learn/graphql/intro-graphql/graphql-vs-rest/
特徴 | REST | GraphQL |
---|---|---|
オーバーフェッチの解消 | ❌ | ✅ |
アンダーフェッチの解消 | ❌ | ✅ |
エンドポイントの管理の容易さ | ❌ | ✅ |
エンドポイントの実装の単純さ | ✅ | ❌ |
単一の HTTP POST (読み取りのみなら GET) リクエストで複数リソースを操作できる その代わり、エンドポイントの実装は REST での実装より複雑な傾向がある
従来の REST はどちらかというとサーバー側の都合 シンプルなデータ構造が役に立ってきた
現代のモバイルアプリケーションの開発はクライアントが中心の世界 データの問い合わせのより強力な表現が重要になってきた
"GraphQL: Client-Driven Development" @beyang (2017) https://about.sourcegraph.com/graphql/graphql-client-driven-development/
単一リクエストを支える GraphQL エンドポイントの構築と正確なデータ構造の維持 クライアントアプリケーションを実行するプラットフォームの多様化が背景
データ構造と操作を宣言するスキーマ言語
"""ポケモンを表します"""
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 r = apolloClient.query(pokemonQuery).await()
Log.d(r?.data?.pokemon?.classification)
// Swift
apollo.fetch(query: pokemonQuery) { result in
guard let data = try? result.get().data else { return }
print(data.pokemon?.classification)
}
クライアントアプリケーションの設計変更に対応するためのツールが提供されている
- GraphiQL … GraphQL の開発環境
- Public GraphQL APIs … 公開されている GraphQL API の一覧
- GraphQL Code Generator … 自動コード生成
- Hasura … GraphQL サーバー
公開されている GraphQL API 一覧の紹介 GraphQL がどういうものか実際に試してみるのに便利
たとえば
https://apis.guru/graphql-apis/
コードの生成
$ graphql-codegen
使う
import { usePokemonQuery } from "./generated";
export default () => {
const { data } = usePokemonQuery();
return data?.pokemon?.classification;
};
https://www.graphql-code-generator.com
GraphQL サーバー 接続したデータベースを自動的に GraphQL API として提供
- GraphQL とは API の問い合わせ言語
- 特徴
- 単一リクエスト … クライアントの無駄なやり取りを減らせる
- 型システム … プラットフォームを問わない
- 便利なツール … 設計変更しやすい
内容
- GraphQL の操作
- query 操作によるデータの取得
実際に GraphQL API からデータを取得してみる
3 種類の操作
- query - 読み取り
- mutation - 書き込み
- subscription - イベントストリーム
1 つのリクエストに複数の操作を含めることができる
実際に 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
または
- https://graphql-pokemon2.vercel.app にアクセス
- 下記の 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
または
- https://graphql-pokemon2.vercel.app にアクセス
- 下記の Query を入力 > 実行 (▶) を選択
query {
pokemon(name: "Pikachu") {
classification
}
}
{
"data": {
"pokemon": {
"classification": "Mouse Pokémon"
}
}
}
ポケモンに関する情報を JSON で取得できた
query {
pokemon(name: "Pikachu") {
classification
}
}
query
… 操作
pokemon
, classification
… フィールド
name
… 引数
"Pikachu"
… 値
フィールドにフィールドを追加することで子孫関係を取得できる
query {
pokemon(name: "Pikachu") {
classification
height {
minimum # <=
maximum # <= これらのフィールド
}
}
}
height
フィールドの中の minimum
, maximum
フィールド
Pokémon GraphQL API https://graphql-pokemon2.vercel.app
ヒント
{
...}
pokemons(first: 151) {
...}
またはpokemon(name: "Pikachu") {
...}
- Control-Space (or Shift-Space)
- フィールドをクリック
- 変数 … Query を再利用できる
- 操作名 … 複数の操作を識別できる
- エイリアス … フィールドに名前を付ける
- フラグメント … いくつかのフィールドをまとめる
- ディレクティブ … Query を修飾できる
変数を使うことで Query を再利用できる
query ($name: String!) { # <= 変数の定義
pokemon(name: $name) { # <= 変数の使用
classification
height {
minimum
maximum
}
}
}
$name
… 変数 (例えば { "name": "Pikachu" }
によって代入)
String!
… 型
操作に名前を付けることで複数の操作を識別できる
query fetchPokemonNames { # <= 操作に名前を付ける
pokemons(first: 151) {
name
}
}
query fetchPikachu { # <= 操作に名前を付ける
pokemon(name: "Pikachu") {
classification
}
}
fetchPokemonNames
, fetchPikachu
… 操作名
フィールドに名前を付ける
query {
pikachu: pokemon(name: "Pikachu") { # <= フィールドに名前を付ける
classification
}
}
取得結果
{
"data": {
"pikachu": { // <= 名付けたプロパティで取得できる
"classification": "Mouse Pokémon"
}
}
}
いくつかのフィールドをまとめ、そのフィールドを取得する際に使用できる
# フラグメントの定義
fragment dimension on PokemonDimension {
minimum
maximum
}
query {
pokemon(name: "Pikachu") {
classification
height {
...dimension # <= フラグメントの使用
}
weight {
...dimension # <= フラグメントの使用
}
}
}
Query を修飾できる
query ($showClassification: Boolean!) {
pokemon(name: "Pikachu") {
classification @include(if: $showClassification)
}
}
変数
{
"showClassification": true
}
@include
ディレクティブは条件に応じてフィールドを含めるかどうかを決める
これ以外にも、いくつかディレクティブがある
- 基本的な構文
- 操作
- フィールド
- 引数と値
- 子孫関係
- 発展的な構文
- 変数
- 操作名
- エイリアス
- フラグメント
- ディレクティブ
- 山本陽平「Web を支える技術」 - HTTP の基礎知識、REST
- Eve Porcello、Alex Banks「初めての GraphQL」
- GraphQL | A query language for your API
- How to GraphQL - The Fullstack Tutorial for GraphQL
- Fullstack GraphQL Tutorial Series | Learn GraphQL Frontend and Backend
セキュリティの懸念事項は一般的な Web サービスと同様に存在
GraphQL 仕様に含まないので一般的な Web の認証・認可の設計と同様に行う
GraphQL にはグローバルなオブジェクトの識別子の宣言によるキャッシュ機構がある
もし HTTP GET メソッドを使用する場合、HTTP キャッシュを利用できる
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
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.1.0/build/highlight.min.js"></script> <script>module={};</script> <script src="https://cdn.jsdelivr.net/npm/highlightjs-graphql@1.0.2/graphql.min.js"></script> <script>hljs.registerLanguage("graphql", hljsDefineGraphQL);hljs.highlightAll();</script> <script> window.addEventListener("DOMContentLoaded", function () { document.querySelectorAll("a")?.forEach(function (a) { a.setAttribute("target", "_blank"); a.setAttribute("rel", "noreferrer"); }); }); </script>