フロントエンドエンジニアが最低限知っておきたい gRPC のこと

# はじめに

受託開発企業でフロントエンドエンジニアを1年ほどやっているものです。直近2ヶ月間 gRPC を使用する案件にアサインされたのですが、「フロントエンドエンジニア視点からみた gRPC 」 に関する記事が少なく、キャッチアップするのにかなり苦労しました。この記事では「フロントエンドエンジニア」 が gRPC について最低限知っておかないといけない概念や技術などをざっくり解説したいと思います。

# この記事の対象者
- フロントエンドエンジニアで gRPC 案件に突然アサインされた人
- フロントエンドエンジニアで gRPC について知りたい人
- フロント側から見た gRPC について知りたい人

# この記事で触れないこと
- メリット / デメリットとか
- マイクロサービスがどうこうとか
- REST との比較とか
- その他の深い内容

# おさらい

この記事に辿り着く前に色々調べて既にご存知の方もいらっしゃるかと思いますが、基本的なことだけおさらいしておきましょう。gRPC について「基本的なことは知ってるよ！」って人は飛ばしてもらって大丈夫です。

## gRPC とは？

> Google が開発した、RPC (Remote Procedure Call) のためのプロトコル

筆者はこの時点で理解不能でした。そもそも 「RPC」 ってなんやねん。

## RPC とは？

- Remote = 遠隔の
- Procedure = 手続き（メソッド）を
- Call = 呼び出す（実行する）

上記の略称のこと。ネットワーク上に接続されたほかの端末のプログラムを呼び出し、実行することができます。と言ってもまだ分かり辛いと思うので公式サイトから拝借しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/08481d0e-87a7-b620-dc1f-0d07ef42f76c.png)

引用元: https://grpc.io/docs/what-is-grpc/introduction/

上記の例では、「C++ で実装したサーバーサイドのメソッド」を「Ruby」や「Andoroid-Java」のクライアント側から実行している例を指しています。この他にも、サーバーサイドを Java で実装し、クライアント側を Go や Python、もちろん**ブラウザ側で動く Typescript や Javascript** で実装することも可能です。

# 結局何を知っておけばいいのか

gRPC の説明は以上です。上記で述べたようなことはどこにでも書いてるのですが、私たち「フロントエンドエンジニア」は何を知っておけばいいのでしょう？ 
 私は上記の概念に加えて、3つのことを知っていれば最低限大丈夫かなと思います。

① Protocol Buffer がコードを自動生成してくれる
② ブラウザ側から gRPC を使うには gRPC-web を使う
③ Envoyと呼ばれるプロキシサーバーが必要である

それでは順番に見ていきましょう

## ① Protocol Buffer がコードを自動生成してくれる

### ①-1 Protocol Buffer とは

そもそも Protocol Buffer とはなんでしょうか。
Protocol Buffer とは要素や属性など、**構造**を定義するための言語のことです。スキーマ言語とも呼ばれたりします。**OpenAPI（swagger）やJSONSchema、GraphQL Schemaの仲間**だと思ってもらえれば大丈夫です。Protocol Buffer については自分も昔記事を書いたのでよければご覧くださう（~~そしてLGTMを押してください~~）

https://qiita.com/takengineer1216/items/726e10acff0518f71cf4


### ①-2 proto ファイル

Protocol Buffer でスキーマを定義するためには　.proto ファイル というものが必要になります。以下はタスクを作成するメソッドと、タスクを取得するメソッドを protoファイルを用いて定義した例です。Proto ファイル

<details>
<summary>コードの全体像（長くなるので折りたたみました）</summary>

```proto
syntax = "proto3";
package sample;


service TaskService {
  rpc CreateTask(CreateTaskRequest) returns (CreateTaskResponse){};
  rpc ReadTaskList(ReadTaskListRequest) returns (ReadTaskListResponse){};
}

message CreateTaskRequest {
  string title = 1;
  string content = 2
};

message CreateTaskResponse {
  int32 id = 1;
};

message ReadTaskListRequest {
  int32 id = 1;
}

message ReadTaskListResponse {
  int32 id = 1;
  string title = 2;
  string content = 3;
}
```
</details>

1つずつ解説していきます。proto ファイルの書き方まで詳細に説明するとキリがないので今回は最低限の解説にとどめておきます。Protoファイルの書き方は以下の記事がわかりやすかったです ⏬

https://qiita.com/Captain_Blue/items/b7a1f4a42f48559fac0c

まず初めに Service を用いてメソッドを定義します。

```proto
service TaskService {
  // タスク作成メソッド
  rpc CreateTask(CreateTaskRequest) returns (CreateTaskResponse){};
  // タスク取得メソッド
  rpc ReadTaskList(ReadTaskListRequest) returns (ReadTaskListResponse){};
}
```

1つ目のCreateTask メソッドは「引数に`CreateTaskRequest`」をとり、「`CreateTaskResponse`」が返されるメソッドになります。同様に2つ目の `ReadTaskList` は 「引数に `ReadTaskListRequest`」をとり、「`ReadTaskListResponse`」が返されるメソッドになります。

次に message を用いて引数に取る型、返り値の型を定義します。

- `CreateTaskRequest`
- `CreateTaskResponse`
- `ReadTaskListRequest`
- `ReadTaskListResponse`

の4つになります。

```proto
message CreateTaskRequest {
  string title = 1;
  string content = 2
};

message CreateTaskResponse {
  int32 id = 1;
};

message ReadTaskListRequest {
  int32 id = 1;
}

message ReadTaskListResponse {
  int32 id = 1;
  string title = 2;
  string content = 3;
}
```

まとめると、

- タスクを作成するためには title と content が必要になる
- 作成した結果返り値として id が返される
- タスクを取得するためには id が必要になる
- レスポンスとしてとして id, title, content が返される

といった感じになります。

### ①-3 proto ファイルから Typescript へコンパイル（本題）

前置きが長くなってしまいましたがここからが本題です。先ほど作成した proto ファイルですが、実は　Typescript のコードへコンパイル（変換）することができます。この記事ではコンパイル方法の詳細は省きますが、詳細は以下の記事がわかりやすかったです ⏬

https://zenn.dev/ryo_kawamata/articles/ts-from-protocol-buffers

proto ファイルから生成された Typescript のコードは以下のように自動生成されます。

```
├── generated // proto ファイルをもとに以下4ファイルが自動生成される
│   ├── task_pb.d.ts
│   ├── task_pb.js
│   └── taskClientPb.ts
└── proto
    └── task.proto // 先ほど作成した proto ファイル
```
フロントエンドエンジニアは 「proto から生成された Typescript（Javascript） ファイル」を用いて、実装を進めていくことになります。余談ですが筆者は「Typescript ファイルが自動生成される」ということを知らずに中のコードを必死に読解しようとしていました。生成された中身はあんまり気にしなくてもいいと思います。（実際理解してなくても実装はできたので、、）

## ② ブラウザ側から gRPC を使うには gRPC-web を使う

Typescript のコードが生成されたので実装を進めていきたいところですが、本来 gRPCをWebブラウザ側から使用するには gRPC-web というものが必要になります。
以下は公式サイトから引用、翻訳したものです。

> gRPC では、サービスを .proto ファイルで定義し、gRPC がサポートする任意の言語でクライアントとサーバを実装することができる。gRPC-Web は、このように構築された gRPC サービスに、ブラウザから慣用的な API を使用してアクセスすることができます。

参考: https://grpc.io/docs/platforms/web/basics/#why-grpc

フロントエンド開発で gRPC を使用する場合は gRPC-web をインストールしておきましょう。

```
$ npm i grpc-web
```

https://github.com/grpc/grpc-web

## ③ Envoyと呼ばれるプロキシサーバーが必要である

gRPC-webがあれば ブラウザからも安易にアクセスでき、、、、ればいいのですがそうもいかないらしいです。ブラウザの制限により、Web アプリケーションは gRPC プロトコルとは異なるプロトコルを実装しています。そのため ブラウザから直接gRPCサーバに接続することはできず、プロキシを挟む必要があります。プロキシサーバーは Envoy というものが公式で推奨されているのでよく使われているそうです。

つまり最終的な構成は以下の図のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/249e81cd-62a1-cd51-02dd-0211221ea1ac.png)
引用元: https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880

# フロント側の実装を見てみる

最後にフロント側の実装コードを見てみましょう。

## タスクを作成するメソッドを実行する

コードの全体像は以下の通りです。

```ts
import { TaskClient } from './generated/taskServiceClientPb';
import { CreateTaskRequest } from './generated/task_pb';

const client = new TaskClient('http://localhost:8080');
const request = new CreateTaskRequest();

// 引数をセットする
request.setContent('内容をセットするよ');
request.setTitle('タイトルをセットする');

// メソッドの実行
client.createTask(request, {}, (error, response) => {
  if (error) {
    throw new Error();
  }
  console.log('通信が成功しました', response);
});
```
上記のコードを解説していきます。

proto ファイルで定義したように、タスクを作成するために必要な引数は `title` と `content` でしたね。引数をセットするために `CreateTaskRequest()`が自動生成されているのでインスタンス化します。

```ts
import { CreateTaskRequest } from './generated/task_pb';

const request = new CreateTaskRequest();
```

`request` には　`setTitle` と `setContent` の「引数をセットするメソッド」があるのでセットします。

```ts
request.setContent('内容をセットするよ');
request.setTitle('タイトルをセットする');
```

引数をセットしたら実際にタスクを作成する `CreateTask` メソッドを持つ `TaskClient` をインスタンス化します。
```ts
const client = new TaskClient('http://localhost:8080');
```

引数をセット後、`createTask`メソッドを実行します。
```ts

// 引数をセットする
request.setContent('内容をセットするよ');
request.setTitle('タイトルをセットする');

// メソッドの実行
client.createTask(request, {}, (error, response) => {
  if (error) {
    throw new Error();
  }
  console.log('タスク作成に成功しました', response);
});
```

## タスクを取得する

詳細な解説は上と同じなので省略します。全体だけ ⏬

```ts
import { TaskClient } from './generated/taskServiceClientPb';
import { CreateTaskResponse } from './generated/task_pb';

const client = new TaskClient('localhost:8000');
const request = new CreateTaskResponse();

// 引数（id）をセット
request.setId('id12345');
// タスクの取得
client.readTaskList(request, {}, (error, response) => {
  if (error) {
    throw new Error();
  }
  console.log('タスク取得に成功しました', response);
});

```

ちょっと記述の仕方にクセがありますが、こんな感じで記述していきます。公式サイトにもサンプルコードがあるのでよければ覗いてみてください。

https://grpc.io/docs/platforms/web/basics/#write-js-client-code

# 最後に

長くなってしまいましたが以上になります。フロントエンドエンジニア視点からのgRPCの解説記事が少なくキャッチアップに苦労しました。自分は1年間フロントエンド開発を行っており、バックエンドの開発経験は（実務では）ほとんどありません。だからこそ純粋なフロントエンドエンジニア目線から gRPC の記事を書けたんじゃないかと思います。間違っていること、認識が甘い箇所などあればご指摘いただけると幸いです。

あとTwitterやってるのでフォローしてもらえると嬉しいです。笑

https://twitter.com/takengineer1216


おしまい

# 参考サイト

https://grpc.io/

https://mypage.otsuka-shokai.co.jp/contents/business-oyakudachi/words/rpc.html


https://grpc.io/docs/platforms/web/basics/#write-js-client-code

https://grpc.io/docs/what-is-grpc/introduction/

https://github.com/grpc/grpc-web