GraphQLを利用したデータ通信の仕組みを一から解説する
株式会社Sally所属エンジニアの @wellPicker です。
今回は、弊社が開発しているマーダーミステリーアプリ「ウズ」において、データ通信が行われる仕組みを一から解説したいと思います。
解説とは言いましたが、本当は自分が勉強したことをまとめる備忘録です。自分にとっての分かりやすさを最優先で書いているので、かなり幅広い話を一つの記事内で全て書きます。また何か新しいことを学んだら、随時更新していく予定です。マサカリは随時受け付けています。
この記事では、初学者でもなるべく分かりやすいようにたとえ話をたくさん使っていこうと思います。
と言うのも、恥ずかしながら自分自身がこの記事を書き始めた時点ではほとんど何も知らない(正確に理解できていない)状態だったからです。
たとえ話はこのような引用ブロックの形で表現するので、必要に応じて読み飛ばしてください。
この記事の目的
この記事では、ウズ上でデータの通信、やり取りをする仕組みを解説したいと思います。
この記事で話すこと
・データベース上のデータに基づいて、クライアント側とサーバ側でデータの通信が行われる仕組み
・GraphQLの基本的な説明
・flutter(フロントエンド)/go(バックエンド)と言う構成でGraphQLを利用する方法
この記事で話さないこと
・GraphQLとREST APIの比較など、GraphQLのメリットに関する話
・その他、ウズで使用されていない技術に関する話
はじめに
データ通信には、クライアントとサーバが登場します。
clientは直訳すると「顧客」であり、serverはレストランなどにおいては(料理を)「出す人」を意味します。データの通信においては、クライアントが何かのデータをリクエストして、リクエストを受けたサーバがクライアントにデータを渡す、ということが行われます。
ウズのデータは実際にはデータベース上で管理されているのですが、クライアントが直接データベースを参照することはありません。代わりに、サーバがデータベースを参照して、それに基づいてクライアントに渡すデータを作成します。
レストランの話が出たので、たとえ話もそのままレストランの話にします。
クライアントが「お客さん」、サーバが「料理人」、データベースは「食糧庫」に相当します。この記事では、お客さんが注文(リクエスト)した料理(データ)が届くまでの過程や、それに関連する話を説明します。
通信プロトコルについて
http通信とは
httpとは Hypertext Transfer Protocol の略称であり、サーバとクライアントが通信をする際に使用する通信プロトコルの一種です。
通信プロトコルとは、通信をする際の規則のようなものです。
httpという通信プロトコルにしたがってサーバーとクライアントの間で行われる通信のことを、http通信と呼びます。
http通信は、一般的に
「クライアントからサーバに対してhttpリクエストを送信する」
「サーバ側で、受け取ったリクエストに基づいてhttpレスポンスを作成する」
「サーバからクライアントにhttpレスポンスを送信する」
という流れで行われます。
http通信では、クライアント側からリクエストが受け取らない限り、サーバから勝手にデータやレスポンスが送られることがありません。
通信プロトコルは、「注文の方法」が一番近いと思います。
http通信は、「このレストランでは、お客様が注文してから初めて料理を提供します」という決まりのことです。
※webでよく見るhttpsとは、httpに加えて「データを暗号化する」と言う追加ルールが加わった通信プロトコルです。
websocketとは
その性質上、http通信はリアルタイムでのデータのやり取りにはあまり適していません。
例えば、データベースに何か変更があったとしても、クライアント側では自発的にリクエストをしない限りその変更を認識することができません。http通信でどうしてもリアルタイムにやり取りをしたい場合、毎秒httpリクエストを送信し続けるなどの方法を取る必要があり、少し非現実的です。
リアルタイムでのデータのやり取りにより適した通信プロトコルとして、websocketがあります。
websocketは、クライアントとサーバの間で双方向に通信を行うためのプロトコルです。
はじめに「通信用のコネクションを作って欲しい」というhttpリクエストをします。このリクエストのことをハンドシェイクと言います。ハンドシェイクによってコネクションを確立した後は、そのコネクション上でデータのやり取りを行います。http通信と異なり、クライアント側からリクエストを受け取らなくても、サーバ側から自発的にデータを送信できることが特徴です。
websocket通信の流れをレストランで例えるなら、以下のような感じになると思います(実際にはこんなレストランはないでしょうが……)。
1.お客さんが「座席(=コネクション)」を用意して欲しいと注文する(ハンドシェイク)
2.料理人が、座席を用意してくれる
3.座席に座ったお客さんは自分から料理を注文することもできるが、座っているだけで料理人の方から勝手に「実はさっき活きのいいカニが手に入ったんですよね〜」みたいな感じで料理を持ってくる
GraphQLと通信プロトコルの関係性
GraphQLはAPI向けのクエリ言語の一種です。QLはQuery Languageの略です。
APIはApplication Programming Interfaceの略であり、ソフトウェア同士がデータをやり取りする際に使用するインターフェースのことを指します。
実際にはREST APIなど、GraphQL以外にもAPIを定義する方法はあります。
ちなみにSQLなどに代表されるデータベース向けのクエリ言語は、API向けのクエリ言語とは異なり直接データベースに干渉するための言語です。今回は特に解説しません。
APIはレストランにおけるメニュー表であり、クエリ言語は料理を注文する際に使用する言語のことです。注文を料理人に伝えるためには、何かしらの言葉を使う必要があります。黙っていたり、「あー!」とか「うー!」とか叫んだりするだけでは注文内容は料理人には伝わりません。
GraphQLは、メニュー表を書いたり、その注文を料理人に伝える言語の一つです。
GraphQLのクエリには以下の3種類があります。
・Query:データを取得する。
・Mutation:データを更新する。
・Subscription:イベントをリアルタイムで監視しながら、データを取得する
実はこのレストランでは単純に料理を注文する以外の注文もできます。
Queryは「この料理をください」に相当します。
Mutationを使うことで、例えば「食糧庫にあるイカを全部タコに変えてください」みたいな注文ができます。
Subscriptionはデータを取得するという意味ではQueryと同じですが、リアルタイムに更新されるのが特徴です。
Queryが「今作れる一番美味しい料理をください」という注文であるのに対し、Subscriptionは「一番美味しい料理をください。食糧庫の食材が変わったら、変更のたびにその料理を通知してください」という注文です。
一般的には、Query/Mutationではhttp通信を利用します。Subscriptionはリアルタイムでの通信が必要になるため、websocket通信を利用しています。
gqlgenとは
ウズのサーバーサイド(バックエンド)は利用して実装しています。
gqlgenとはGo言語のライブラリの一種であり、GraphQLサーバーを自動生成することができます。
もう少し具体的に書くと、GraphQLスキーマを定義することで、
・~/resolver.go(実際にデータベースとやり取りして、データを返すための処理を書くファイル)
・~/generated.go(GraphQLサーバーが受け取ったリクエストを元に、resolverの適切なメソッドを呼び出すためのファイル)
・~/models_gen.go(GraphQLスキーマ内で定義されたtypeなどの情報に基づいて、Go言語の構造体として定義するためのファイル)
を自動生成することができます。
これによって、resolver.goで実際の処理内容を書くだけで、APIリクエストを受け取ってデータを返すGraphQLサーバを作成することができます。
サーバ側で定義するGraphQLスキーマは、そのレストランで注文可能な料理の一覧です。
gqlgenは、料理人向けのマニュアル(を綴じて本の形にしてくれる人)です。マニュアルがあることで、お客さんから注文を受けた料理人がマニュアルの対応するページを探すことができるようになります。resolver.go内で実際の処理内容を定義する行為は、マニュアルのページに具体的な調理方法を書くことに相当します。
クライアント(フロントエンド)側の実装
ウズのフロントエンドはflutterを利用して実装しています。flutterでGraphQLを利用するために、graphql, graphql_codegenのパッケージを利用しています。
これらのパッケージを利用することで、flutter側で定義したGraphQLスキーマからコードの自動生成をおこなってくれます。
GraphQLのレスポンスはJSON形式で返されます。graphql_codegenパッケージを使うことで、このJSONデータを扱うためのモデルクラスが自動生成されます。このクラスを利用することで、データを型安全に操作できます。
クライアント側で定義するGraphQLスキーマは、実際にお客さんが注文する内容です。
まとめ
今回は、ウズで使用されているアーキテクチャにおける、GraphQLを用いたデータ通信の仕組みについて解説しました。
自分と同じように、GraphQLって結局何やねん? どうやって情報をやり取りしてるの? と言う疑問を持っている人の参考になれば幸いです。
Discussion