Slide 1

Slide 1 text

開運研修2019 スキーマファースト開発入門 GraphQL 編 ymmt

Slide 2

Slide 2 text

Agenda ▌問い合わせ言語とは ▌GraphQL 概説 ▌GraphQL サーバーの実装 ▌GraphiQL でクエリを楽々開発 ▌sabakan / CKE 連携の実装例 ▌GraphQL vs OpenAPI vs gRPC

Slide 3

Slide 3 text

問い合わせ言語(Query Language)とは ▌利用者が指定した情報をデータベースから得る記法 ⚫ SQL, LDAP, JSON Pointer, … ▌対象とするデータモデルに強く依存 ⚫ SQL = Relational モデル ⚫ LDAP = LDAP Schema ⚫ JSON Pointer = JSON

Slide 4

Slide 4 text

問い合わせ言語がなぜ必要か データベースの内部実装を隠蔽したい • B-Tree で実装されているといった内容は隠したい 複雑なデータ取得処理を何度も実装したくない • 汎用の問い合わせ(Query)実行エンジンさえあれば、 問い合わせをテキストで書くだけで済む 同じ問い合わせ言語で複数のシステムを利用したい • SQL を学べば、広範囲に役立つ

Slide 5

Slide 5 text

GraphQL とは ▌Facebook が 2015 年に公開 ⚫ 2018 年 11 月に GraphQL Foundation に移管 ⚫ graphql.org で仕様などを公開 ▌SQL に少し似たスキーマとクエリ言語の仕様 ▌可能な操作 ⚫ データの取得(Query) ⚫ データの変更(Mutation) ⚫ イベント購読(Subscription)

Slide 6

Slide 6 text

GraphQL の特徴 • データモデルと Query, Mutation, Subscription を定義 • スキーマからサーバー実装・クライアント実装の一部を自動生成可能 Schema First • 無限に複雑なクエリが書ける (DoS 注意) • 関連するデータを一つのクエリで一括取得できる データモデルが循環参照できる • 一般的な実装では応答形式は JSON • ブラウザやモバイルの画面描画で便利 JSON との親和性

Slide 7

Slide 7 text

GraphQL の採用例 ▌Facebook ⚫ https://developers.facebook.com/docs/graph-api/ ▌GitHub API v4 ⚫ https://developer.github.com/v4/ ▌AWS AppSync ⚫ https://aws.amazon.com/appsync/ ▌Kibela ⚫ https://github.com/kibela/kibela-api-v1-document

Slide 8

Slide 8 text

スキーマの書き方

Slide 9

Slide 9 text

スキーマ定義言語(SDL) ▌データ型と操作の型を定義する ▌データ型の種別 ⚫ Scalars(Int, String, Float, Boolean, ID, カスタム) ⚫ Enum ⚫ List ⚫ Objects / Input types ▌すべての型は nullable ⚫ Non-null な場合「String!」のように ! をつける

Slide 10

Slide 10 text

Objects ▌各フィールドは実質的にメソッドの定義 ⚫ 引数が持てる enum Instrument { PIANO FLUTE } type Person { name: String! age: Int! friends: [Person!] canPlay(inst: Instrument!): Boolean! }

Slide 11

Slide 11 text

Input types ▌クエリで渡せる値はスカラ・Enum・もしくはイン プット型のみ ▌インプット型のフィールドは引数を持たない input SearchCondition { names: [String!] minAge: Int } type Query { searchPerson(cond: SearchCondition!) [Person!]! }

Slide 12

Slide 12 text

Objects vs Input types ▌Objects の各フィールドはサーバーサイド処理の宣言 ⚫ 各フィールドは値を返す resolver として実装 ▌Input types は単なるデータフォーマット ⚫ JSON へのシリアライズ・デシリアライズを実装 ⚫ custom scalar も同じ

Slide 13

Slide 13 text

Root types ▌スキーマは、可能な操作を列挙する必要がある ▌それぞれ Query, Mutation, Subscription という type のオブジェクトで指定する type Query { searchPerson(cond: SearchCondition!) [Person!]! } type Mutation { addPerson(name: String!, age: Int!) Person! declareFriend(p1: String!, p2: String!) Bool! }

Slide 14

Slide 14 text

ドキュメント ▌スキーマの各要素の前に説明を文字列で書ける ▌文字列は markdown で書ける ””” Person represents a **human** being. ””” type Person { name: String! age: Int! friends: [Person!] canPlay(inst: Instrument!): Boolean! }

Slide 15

Slide 15 text

クエリの書き方

Slide 16

Slide 16 text

GraphQL クエリとは ▌Query や Mutation を呼び出す書式 ▌一度に複数呼び出すこともできる(Aliases) ▌クエリ自体を関数化することもできる ▌受け取るオブジェクトのフィールドは必ず指定が必要 ⚫ SELECT * はできない

Slide 17

Slide 17 text

Simplified Query { searchPerson(cond: {minAge: 30}) { name age friends { name friends { name } } } }

Slide 18

Slide 18 text

Non-simplified Query query Search { searchPerson(cond: {minAge: 30}) { name age } }

Slide 19

Slide 19 text

Multiple queries query MultiSearch { over30: searchPerson(cond: {minAge: 30}) { name age } mitz: searchPerson(cond: {names: [”mitz”]}) { name age } }

Slide 20

Slide 20 text

Variables ▌Query のパラメーターを $var で変数化できる ▌変数は別途 JSON で与えられる query Search($cond: SearchCondition!) { searchPerson(cond: $cond) { name age } } --- { ”cond”: {”age”: 30} }

Slide 21

Slide 21 text

任意のキーバリューはどう扱うの?

Slide 22

Slide 22 text

name/value ペアのリストで扱う type Label { name: String! value: String! } type Person { name: String! labels: [Label!]! }

Slide 23

Slide 23 text

GraphQL サーバーの実装

Slide 24

Slide 24 text

サーバー実装 ▌各言語に多数のライブラリが存在 ⚫ https://graphql.org/code/#server-libraries ▌Schema First 開発に向いていそうなもの ⚫ Node: Apollo ⚫ Go: gqlgen ⚫ Java: GraphQL Java Tools ⚫ PHP: Siler

Slide 25

Slide 25 text

gqlgen ▌スキーマから Go のコードを自動生成 ▌自動で生成できなかったレゾルバだけ実装すれば動く ▌生成例 ⚫ https://github.com/ymmt2005/graphql-example

Slide 26

Slide 26 text

GraphiQL でクエリを楽々開発

Slide 27

Slide 27 text

GraphiQL とは ▌https://github.com/graphql/graphiql ▌JavaScript 製の GraphQL クエリ開発 IDE ▌gqlgen 等にも簡単に組み込める ▌Let’s try! ⚫ go run github.com/ymmt2005/graphql-example/server ⚫ http://localhost:8080/ にアクセス ⚫ ここまでに出てきたクエリを入力してみよう!

Slide 28

Slide 28 text

sabakan / CKE 連携の実装例

Slide 29

Slide 29 text

sabakan と CKE の関係 ▌sabakan ⚫ 物理サーバーの情報を一元管理 ⚫ ネットブート機能もある ▌CKE ⚫ Kubernetes クラスタを自動構築 ⚫ 利用する物理サーバーの情報は外部から提供 ▌CKE -> sabakan ⚫ CKE が sabakan に利用可能機材を問い合わせ ⚫ 機材の条件を人間が指定したい

Slide 30

Slide 30 text

GraphQL を利用 ▌sabakan ⚫ GraphQL API を実装 ⚫ 機材を複雑な条件で検索可能にした ▌CKE ⚫ GraphQL クライアントを実装 ⚫ クエリは固定 ⚫ 検索条件は変数化 ⚫ 人間は検索条件を JSON で指定可能

Slide 31

Slide 31 text

クライアントの試験 ▌CKE の単体テストで sabakan 連携をテストしたい ▌スキーマから自動生成したモックサーバーでテスト ⚫ Resolver で適当なものを返すだけ! ⚫ https://github.com/cybozu-go/cke/tree/master/sabakan/mock

Slide 32

Slide 32 text

GraphQL vs OpenAPI vs gRPC

Slide 33

Slide 33 text

OpenAPI とは ▌REST API 用のスキーマ ⚫ 旧 swagger ⚫ YAML/JSON で書く ▌見てのとおり、複雑 openapi: "3.0.0" info: title: Simple API overview version: 2.0.0 paths: /: get: operationId: listVersionsv2 summary: List API versions responses: '200': description: |- 200 response content: application/json: examples: foo: value: { "versions": [ { "status": "CURRENT", "updated": "2011-01-21T11:33:21Z", "id": "v2.0", "links": [ { "href": "http://127.0.0.1:8774/v2/", "rel": "self" } ] }, { "status": "EXPERIMENTAL", "updated": "2013-07-23T11:33:21Z", "id": "v3.0", "links": [ { "href": "http://127.0.0.1:8774/v3/", "rel": "self" } ] } ] } '300': description: |- 300 response content: application/json: examples: foo: https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/api-with-examples.yaml

Slide 34

Slide 34 text

比較表 GraphQL OpenAPI gRPC スキーマファースト Yes! 不可能ではない Yes! スキーマが書きやすい Yes! No Yes! クエリが手書きできる Yes! No (curl?) No ストリーミング サーバーのみ No 双方向 成熟している No Yes? Yes BLOB No (拡張はある) Yes No IDE GraphiQL 等 swagger-editor 等 RPC なので不要 ブラウザからの利用 Yes Yes gRPC-Web が必要 Versioning 普通しない 普通する 普通しない 性能 ひとつのクエリにまとめ る最適化ができる やり方次第 やり方次第

Slide 35

Slide 35 text

個人的所見 ▌GraphQL は Query にとどめるのが無難 ⚫ Mutation の応答内容をクエリ書式でかける柔軟性は多くの 場合不要なので、単に実装コストが高くつく ▌OpenAPI はコードファーストでスキーマ自動生成が楽 ⚫ Kubernetes API 方式 ⚫ https://www.blazemeter.com/blog/how-to-generate-openapi-definitions-from-code/ ▌gRPC が総合的に楽なので、なるべく使う ⚫ BLOB については REST を併用