Slide 1

Slide 1 text

NuxtJS + REST APIで 運用中サービスをNuxtJS + GraphQL に変更したことによる光と影 アウモ株式会社 エンジニアチームマネージャー 村田翔

Slide 2

Slide 2 text

自己紹介 2 • 名前 • 村田 翔 • 担当 • サーバーサイド兼フロントエンドエンジニア • メインはRuby on Rails • aumo歴 • 3年弱(サーバーサイドでは最古参) • 旅行好きなのもあって、aumoに長らく在籍

Slide 3

Slide 3 text

3 ⓘ Start presenting to display the poll results on this slide. aumoはご存知ですか?

Slide 4

Slide 4 text

• おでかけ領域をメインに、旅行やグルメ情報をお届けするメディア • アプリ • Webサービス aumoはご存知ですか? 4 記事サイト 比較サイト <- 今回NuxtJS + GraphQLに変更した対象

Slide 5

Slide 5 text

5 記事 × SNS iOS Android

Slide 6

Slide 6 text

6 記事 × ユーザー生成コンテンツ

Slide 7

Slide 7 text

7 記事 × ユーザー生成コンテンツ × ホテル × 宿泊プラン横断検索

Slide 8

Slide 8 text

8 記事 × ユーザー生成コンテンツ × グルメ

Slide 9

Slide 9 text

9 記事 × ユーザー生成コンテンツ × レジャー・ショッピング

Slide 10

Slide 10 text

1.なぜREST APIからGraphQLに変更したのか 2.GraphQL化して見えたこと • 光 • エンドポイント • 仕様変更 • 影 • N+1 • エラーハンドリンク • ログ解析 今日お伝えすること 10

Slide 11

Slide 11 text

1.なぜREST APIからGraphQLに変更したのか 2.GraphQL化して見えたこと • 光 • エンドポイント • 仕様変更 • 影 • N+1 • エラーハンドリンク • ログ解析 今日お伝えすること 11

Slide 12

Slide 12 text

12 ⓘ Start presenting to display the poll results on this slide. GraphQL使っていますか?

Slide 13

Slide 13 text

• バックエンド • Ruby on Rails • REST API • GraphQL • graphql • graphql-batch • graphiql-rails • フロントエンド • NuxtJS • ログ監視 • Papertrail • Sentry 構成 13

Slide 14

Slide 14 text

構成 14 Amazon EC2 Amazon EC2 ELB サブドメインで 3サイトを同一インスタンスに搭載 フロントエンド バックエンド

Slide 15

Slide 15 text

構成 15 Amazon EC2 Amazon EC2 ELB サブドメインで3サイト搭載 • middlewareでFQDN毎にパスチェック • 各トップページはFQDNを元にコンポーネント出しわけ • 各詳細ページはNuxtJSのディレクトリ規約に沿って設置 フロントエンドでのサイト分割

Slide 16

Slide 16 text

● エンドポイントを極力増やしたくない ● 工数削減 16

Slide 17

Slide 17 text

初期から3サイト作成 という要件なのかというと... 17

Slide 18

Slide 18 text

初期要件 18 • ホテルの比較サイトを作りましょう • 単一サイトなので既存の記事サイト同様にバックエンドはREST API でいいか

Slide 19

Slide 19 text

• グルメのサイトも作りましょう • バックエンドはAPIのエンドポイント増やして対応すればいいか 要件追加 19

Slide 20

Slide 20 text

要件追加 20 • レジャー・ショッピングのサイトも作りましょう • さらに別ジャンルで展開する可能性出てくるなこれ🤔 • APIのエンドポイントを都度増やしていくのは何だかなぁ

Slide 21

Slide 21 text

要件追加 21 • 各サイトを巡回されるような動線になる要素欲しいですね • 各サイト用に用意しているAPIエンドポイントそれぞれ修正いるな • API多いと作業煩雑だし、また類似の要件追加くるだろなこれ 🤔

Slide 22

Slide 22 text

そこで... 22

Slide 23

Slide 23 text

• クエリ言語 • エンドポイント単一 • 必要な情報だけ取得できる • クエリで指定したフィールドのみ返却される • Facebook社が2012年から開発 • 採用している組織はFacebook、GitHub、PayPalなど数百を越す GraphQLとは 23

Slide 24

Slide 24 text

各サイトで必要な情報を共通のエンドポイントで 網羅できるのは、実装が楽になりそうな予感! ※新しい技術を取り入れたい欲有あり 24

Slide 25

Slide 25 text

実際にREST API -> GraphQL化してみた 25

Slide 26

Slide 26 text

1.なぜREST APIからGraphQLに変更したのか 2.GraphQL化して見えたこと • 光 • エンドポイント • 仕様変更 • 影 • N+1 • エラーハンドリンク • ログ解析 今日お伝えすること 26

Slide 27

Slide 27 text

エンドポイント 27 [GET] /api/v1/hotels [GET] /api/v1/hotels/{hotel_id} [GET] /api/v1/hotels/search [GET] /api/v1/gourmets [GET] /api/v1/gourmets/{gourmet_id} ・ ・ ・ ルーティング追加して、 コントローラー追加して、 ビュー追加して・・・ • サイト追加毎に増えていくエンドポイント...

Slide 28

Slide 28 text

• エンドポイント単一 エンドポイント 28 [GET] /api/v1/hotels [GET] /api/v1/hotels/{hotel_id} [GET] /api/v1/hotels/search [GET] /api/v1/gourmets [GET] /api/v1/gourmets/{gourmet_id} ・ ・ ・ ルーティング追加して、 コントローラー追加して、 ビュー追加して・・・ [POST] /graphql スキーマとフィールド更新のみ ×

Slide 29

Slide 29 text

• 各サイトの詳細ページに周辺施設情報の要素追加したい 画面仕様変更への対応 仕様変更 29

Slide 30

Slide 30 text

• REST APIの場合 • バックエンド • 対象エンドポイントのレスポンスに要素追加 or エンドポイント追加 • フロントエンド • 新規要素表示 • エンドポイント追加の場合は新たに呼び出し追加 各サイトの詳細ページに周辺施設情報の要素追加したい 仕様変更 30

Slide 31

Slide 31 text

• GraphQLの場合 • バックエンド • スキーマとフィールド更新 • フロントエンド • 新規要素表示 • クエリに新規で追加する要素を追記 各サイトの詳細ページに周辺施設情報の要素追加したい 仕様変更 31

Slide 32

Slide 32 text

追加の場合はあまり工数的にかわらなそう🤔 各サイトの詳細ページに周辺施設情報の要素追加したい 仕様変更 32

Slide 33

Slide 33 text

• REST APIの場合 • バックエンド • 対象のエンドポイントのレスポンスから削除 or エンドポイント追加 • フロントエンド • 要素削除 各サイトの詳細ページに周辺施設情報の要素削除したい 仕様変更 33

Slide 34

Slide 34 text

各サイトの詳細ページに周辺施設情報の要素削除したい 仕様変更 34 • GraphQLの場合 • バックエンド • そのまま • フロントエンド • 要素削除

Slide 35

Slide 35 text

各サイトの詳細ページに周辺施設情報の要素削除したい 仕様変更 35 • 要素削除の場合はフロントエンドの修正だけでいける • バックエンドは対象の要素をクエリで指定されない限り、その要素を 取得する処理は走らないため無駄が出ない

Slide 36

Slide 36 text

1.なぜREST APIからGraphQLに変更したのか 2.GraphQL化して見えたこと • 光 • エンドポイント • 仕様変更 • 影 • N+1 • エラーハンドリンク • ログ解析 今日お伝えすること 36

Slide 37

Slide 37 text

• スキーマ毎にクエリが走る associationが適切なタイミングでeager loadされていない N+1 37

Slide 38

Slide 38 text

• loadの該当fieldを要求されたときだけeager loadが走り、事 後でWHERE INされる graphql-batchを導入 N+1 38

Slide 39

Slide 39 text

• フロントエンドでレスポンスステータスによるエラーハンドリ ングができない GraphQLは常にレスポンスステータス200を返却する エラーハンドリング 39 ×

Slide 40

Slide 40 text

• catchの中でerror関数を呼ぶ レスポンスステータスによるエラーハンドリングは行わない エラーハンドリング 40

Slide 41

Slide 41 text

• エンポイントが単一なため全て request_uri: /graphql • レスポンス速度の調査などで問題箇所の洗い出しが困難 リクエストパス毎の解析ができない ログ解析 41

Slide 42

Slide 42 text

• リクエストボディ毎に分けることで呼び出し箇所の分類できる • レスポンス速度などに問題がある箇所の特定できる リクエストボディまでログに出力する ログ解析 42

Slide 43

Slide 43 text

まとめ 43

Slide 44

Slide 44 text

● エンドポイントを極力増やしたくない ○ 同じ情報を複数サービスで使い回す場合に有効 ● 工数削減 ○ 画面要件が頻繁に変わるサービスでは効果高い 44 ୡ੒ ୡ੒

Slide 45

Slide 45 text

45