2022年09月07日

Restful API、GraphQL…フロントエンドとバックエンドをつなげる方法

カテゴリー:システム開発, テクノロジー

タグ:api, GraphQL, バックエンド, フロントエンド

Knowledge_seci_model

Webアプリケーション開発時に、フロントエンドとバックエンドを分かれて開発するのはよくあるケースです。そうやって分業で開発されるフロントエンドとバックエンドはどう連携し合うのが良いでしょうか。

この記事はよくあるケースを幾つか紹介します。

RESTful API

RESTとはRepresentational State Transferの略になります。RESTの原則に従って開発されるシステムをRESTfulと言い、そのAPIをRESTful APIと呼びます。

RESTful APIでは、URIとHTTPメソッドの組み合わせによって、何のリソースをどう処理するかを指定します。HTTPメソッドは次のような意味で用います。

HTTPメソッド意味
GETリソースの取得
POSTリソースの作成
PUT/PATCHリソースの更新
DELETEリソースの削除

それに対してURIでは、どのリソースを操作するかを指定します。例として以下のようになります。

  • /users
    ユーザーに対する操作(作成または一覧取得)
  • /users/:userId
    ユーザーID = :userId に対する操作(更新または削除)
  • /items
    アイテムに対する操作(作成または一覧取得)
  • /items/:itemId
    アイテムID = :itemId に対する操作(更新または削除)
  • /users/:userId/items
    ユーザーID = :userId なユーザーが所有するアイテムに対する操作(作成または一覧取得)
  • /users/:userId/items/:itemId
    ユーザーID = :userId なユーザーが所有するアイテム :itemId に対する操作(更新または削除)

そしてHTTPメソッドとURIを並べてAPIを表現します。例えば以下のように書きます。

  • POST /users
    ユーザ作成
  • GET /users/:userId
    ユーザID = :userId の取得
  • PATCH /users/:userId/items/:itemId
    ユーザID = :userId のユーザーが持つアイテム :itemId の更新

RESTful APIの利点

URIを見ればどのリソースを対象にしているか分かり、HTTPメソッドを見ればどういった操作を行うかが分かります。

RESTful APIの欠点

URI(エンドポイント)が分かっていないとAPI操作ができません。また、分かりやすさのためにRESTful原則に反したURIが作成される場合があります。

例)

  • アイテム検索 /items/search
  • ログイン /login

返ってくるデータ(レスポンス)の詳細をクライアント側で制御するのはあまり現実的ではありません。例えばクエリーパラメータで指定したり、別なAPIを作成する必要があります。レスポンスが複雑になればなるほど、この制御は困難になります。

  • クエリーパラメータで制御する例
    GET /users/:userId?fields=name,email,profile
  • APIを分ける例
    • GET /users/:userId
    • GET /users/:userId/more_fields

GraphQL

エンドポイントが分からないとアクセスできない、送受信されるデータの形がドキュメントを見ないと分からないといったRESTful APIの難点を解決すべく作られたのがGraphQLです。元々はFacebookが自社APIで使っていたFQL(Facebook Query Language)になります。このFQLを汎用化したものがGraphQLになります。

image.png

GraphQL | A query language for your API

GraphQLの利点

以下がGraphQLの利点になります。

  1. エンドポイント/HTTPメソッドが1つである
  2. スキーマが用意されている
  3. 取得するデータをクライアントが指定できる

エンドポイント/HTTPメソッドが1つである

GraphQLは一般的にエンドポイントを /graphql とします。すべてのリソースへのアクセスは、この1つのエンドポイントだけで行います。また、アクセスはすべてPOSTメソッドになります(取得、更新、削除すべて)。この点はとても分かりやすいです。

スキーマが用意されている

スキーマとはリクエストや、取得されるデータの形式を定義したドキュメントになります。このスキーマがAPIの1つとして公開されていることで、各プログラミング言語で読み込んだり、プレイグラウンド(テスト実行環境)で自動補完の恩恵に預かれます。

image.png

HexabaseのPlaygroundの例

取得するデータをクライアントが指定できる

最後に、取得するデータをクライアント側で細かく指定できる点もGraphQLの特徴です。例えば以下のように記述します。これはユーザーデータを取得しますが、名前とメールアドレスが欲しい場合です。

query users {
	name
	email
}

上記に加えて、年齢が欲しい場合には次のように記述します。

query users {
	name
	email
	age
}

さらにユーザーに紐付いた商品情報が欲しい場合もあるでしょう。その場合には、以下のように指定します。

query users {
	name
	email
	age
	items {
		id
		name
		price
	}
}

このようにクライアント側からGraphQLの内容を指定することで、そのレスポンスを動的に変更できます。必要な場面に応じて、リクエスト内容を変更することで、必要なデータだけを取得できます。

GraphQLの欠点

逆に欠点としては、技術的にまだ広く浸透しているとは言いがたく、ナレッジがたまっていないことでしょう。何か困ったことがあっても、オンライン上で即座に答えが見つからないかも知れません。

また、ライブラリがまだ多くないのも難点です。例えばJavaScriptであればApollo GraphQLというライブラリが有名です。他の言語でもGraphQLクライアントがありますが、ほぼ素のGraphQLを記述しなければならない点が難点です。

gRPC

image.png

gRPCのWebサイト

RPCというのはRemote Procedure Callの略になります。そしてgRPCというのはGoogleが開発、公開したRPCになります。RPCを簡単に言うと、クライアントからサーバー側のメソッドを指定して実行する技術になります。gRPC登場以前にはXML-RPCやJSON-RPCがありましたが、これらはテキストデータを扱うのに特化していました。例えばWordPressではXML-RPCが利用できましたが、画像アップデートはバイナリファイルをテキスト変換(Base64エンコード)して行っています。

XML-RPC wp « WordPress Codex

gRPCはそれらの難点を解決し、バイナリデータでも扱えるRPCとなっています。

gRPCの特徴

gRPCはXML-PRCやJSON-RPCと異なり、HTTP/HTTPSは利用しません。HTTP/2を使い、データフォーマットはProtocol Buffersを基本としています。ただ、仕様上これらは自由に選択できるようです。

Protocol Buffersはプロトコル定義ファイルを用意します。これはクライアント・サーバー間でやり取りされるデータの定義ファイルになります。そして、このプロトコル定義ファイルを利用して、各種プログラミング言語向けにクラスファイルが生成できます。このクラスファイルを利用することで、クライアント側での開発はとても楽になるでしょう。

同様にサーバー側のサービスについて、サービス定義ファイルも作成します。これによってクライアント側では、サーバー側の呼び出せる機能と、その呼び出し方が分かります。このサービス定義ファイルを使うことで、サーバー側とクライアント側のコードも生成可能です。

Webでの利用

Webアプリケーションの中でgRPCを使う場合にはgrpc/grpc-web: gRPC for Web Clientsを利用します。ただし、素のgRPCと比べて幾分制限があるようなので注意してください(筆者は使ったことがありません)。

一般的にはgRPCはサーバー間通信などで利用するのが便利かと思います。

WebSocket

ブラウザとサーバーで同期通信を行いたい場合にはWebSocketが便利です。ブラウザからサーバーへの送信はもちろん、サーバーからブラウザへメッセージの送信もできます。なお、WebSocketはテキストメッセージのみ送受信可能です。JSONを使う場合には一旦文字列に変換して、受け取った後にパースします。

WebSocketではURIを使って、どのチャンネルを購読(Subscribe)するか指定します。サーバー側では、この購読しているクライアントだけにメッセージを送信することで、例えばチャットルームなどの配信先を限定したメッセージ送信を行えます。

JavaScriptでは WebSocket というオブジェクトを使います。

const ws = new WebSocket('wss://localhost:4000/websocket');

接続されると onopen イベントが実行されます。

ws.onopen = function() {
	// 接続された際のイベント
}

次にメッセージを送る際には send を使います。

ws.send(JSON.stringify({message: "新しいメッセージ"}));

メッセージを受け取る際には onmessage イベントに処理を書きます。

ws.onmessage = function(message) {
	// ここに処理を書きます
}

接続を閉じた際には onclose イベントが呼ばれます。

ws.onclose = function() {

}

サーバーからブラウザに対して何かメッセージを送りたい時に試してください。

Web Push API

サーバーからのメッセージが非同期でも良い場合にはWeb Push APIを利用することもできます。2023年中にはmacOS/iOS/Android/Windows/Linuxと幅広いOS、Webブラウザでサポートされます。2022年7月現在ではiOSでは利用できません(macOSはSafariでは利用できません)。

image.png

Push API | Can I use… Support tables for HTML5, CSS3, etc

Web Push APIではユーザーの承認が必要です。プッシュ通知送信に関する承認が得られると、固有のURIが得られます。これは各ブラウザベンダー毎に異なるもので、例えばChromeの場合はFirebaseのドメインで、デバイストークン(ブラウザ固有のID)を含んだものになります。

サーバー側ではこの固有のURIを保存します。そしてプッシュ通知を送信する際に、このURIに対して送信したいメッセージと共にリクエストを送信します。

ブラウザがメッセージを受け取ると、Service Workerのpushイベントが呼ばれます。

self.addEventListener('push', function(event) {
	// このイベントが呼ばれます
});

この中で通知オブジェクトを作り、表示を行います。同時に、この通知をクリックした際のイベントを設定し、特定のURLに遷移したりします。

data = event.data.json();
const title =  data.title;
const body = data.message;
const icon = data.icon;
const tag = 'タグ';
const notification = new Notification(title, {
	body, icon, tag,
});

notification.addEventListener('click', function() {
	if (clients.openWindow) {
		clients.openWindow('https://example.com/click.html');
	}
});

まとめ

クライアントからサーバー側の処理を呼び出す場合と、サーバー側からクライアントを呼び出す場合で利用できる技術が変わります。ぜひ最適なものを選んでください。個人的に試したことはないのですが、GraphQLにはSubscriptionが用意されており、データが更新されると通知してくれる機能もあるようです(実装系によるとは思います)。

私たちの提供するHexabaseではRESTful APIWebSocketGraphQL(現在アルファ版)を用意しています。アプリの特性、開発要件に合わせて選択してください。

役に立ったら、記事をシェアしてください