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

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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/cbee11e9-2ade-fdb9-258f-197476c201f8.png)


[GraphQL | A query language for your API](https://graphql.org/)

### GraphQLの利点

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

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

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

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/cb156250-9a9c-5643-07a3-9bc64dffaf40.png)


[HexabaseのPlayground](https://hxb-graph.hexabase.com/graphql)の例

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

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

```graphql
query users {
	name
	email
}
```

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

```graphql
query users {
	name
	email
	age
}
```

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

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

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

### GraphQLの欠点

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

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

## gRPC

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/512ce33f-23dc-88c7-e10b-c1e71b5bef59.png)

[gRPC](https://grpc.io/)のWebサイト

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

[XML-RPC wp « WordPress Codex](https://codex.wordpress.org/XML-RPC_wp#wp.uploadFile)

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](https://github.com/grpc/grpc-web)を利用します。ただし、素のgRPCと比べて幾分制限があるようなので注意してください（筆者は使ったことがありません）。

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

## WebSocket

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

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

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

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

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

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

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

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

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

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

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

```js
ws.onclose = function() {

}
```

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

## Web Push API

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/fc48de71-1455-3424-0d88-9c2a2f76b001.png)


[Push API | Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/push-api)

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

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

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

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

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

```js
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 API](https://apidoc.hexabase.com/)と[WebSocket](https://www.hexabase.com/realtime-notification/)、[GraphQL（現在アルファ版）](https://devdoc.hexabase.com/docs/features/graphql)を用意しています。アプリの特性、開発要件に合わせて選択してください。