Node.jsでgRPCを動かそう

※こちらの記事は3/21に開催するxhack勉強会[「Node.jsでgRPCを動かそう」](https://x-hack.connpass.com/event/207013/)の資料となります


# 本日のお品書き
- [始めに](#始めに)
    - 自己紹介
    - 想定参加者について
    - 今日のゴール
- [Node.jsでgRPCを動かそう](#nodejsでgrpcを動かそう)
    - gRPCって何？
    - ハンズオン
    - 質疑応答
- [最後に](#最後に)


# 始めに
**ぜひ#xhack勉強会 で感想をツイートしてください！**
**また、可能な方はカメラオンにして欲しいです！顔が見えない中ひとりで話すのは寂しい、、**
## 自己紹介
**とむ**といいます。
新卒未経験からエンジニアになってまもなく2年。
今年の2月に転職をし、大阪から東京に引っ越してきました。
転職前はJavaを使って主にサーバ側の開発をしていましたが、現在はスマホアプリ開発をメインに行っている[株式会社マンハッタンコード](https://www.mht-code.com/)で働いています。
Twitterは[こちらをクリック](https://twitter.com/tomsan96)←ぜひフォローしてください！

以前からX-HACKさんの勉強会にはものすごくお世話になっていたのですが、今回初めて勉強会を主催させていただきます。
お手柔らかにお願いします、、

## 想定参加者
- gRPCって聞いてピンと来ない人
- JavaScriptが読める人
- サーバ、クライアント、API、HTTPといった用語をある程度理解している人
- Macを持っている人
- ターミナルで基本的なコマンドが扱える人(npm・nodeコマンドがインストールされている人)

## 今日のゴール
- gRPCをふわっと理解する
- [公式チュートリアル](https://grpc.io/docs/languages/node/quickstart/)を参考に実際に動かしてみる

注意：**今回の勉強会では書き方の細かいルールなどは説明しません！**

# Node.jsでgRPCを動かそう

## gRPCって何？
- Googleが公開したRPCフレームワークの一つ

### そもそもRPCって？？？
RPCはRemote Procedure Callの略。
たけがみまさきさん著の[スターティングgRPC](https://booth.pm/ja/items/1315322)では

> Remote Procedure Callを訳すと遠隔手続き呼び出しという意味になります。すなわち、あるサービスからべつのサービスのアプリケーションの処理(サブルーチン/クラス/関数など)を呼び出す技術ということです。

と説明されています。
遠くにある関数とかを呼び出せちゃう便利なやつがRPCで、gRPCはその中の1つということをふんわりと頭に入れておいてもらえたらOKです！

### gRPCのここがすごい

- HTTP/2によって**高速な通信ができる**
- **ProtocolBuffers**
    - データをシリアライズ化する仕組み
    - 独自の書き方(IDL=インターフェース定義言語)で呼び出しの定義ができる！！→異なる言語を使っているサービスの処理も呼び出せる!!!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538274/9644faef-bac7-ebb7-0c11-fc04ec30ecd3.png)





ここからは実際に手を動かしていきましょう！
## ハンズオン
基本的な流れはこちら。
1. 必要なツールやライブラリをインストールする
2. Protocol Buffersを使って定義ファイルを書く
3. (定義ファイルをコンパイルしてコードを生成する)
4. 定義に沿ってサーバとクライアントを実装する

[公式チュートリアル](https://grpc.io/docs/languages/node/quickstart/)を参考に、よりシンプルなものを作ってみましょう

**作るもの**
指定した人に挨拶をするアプリみたいなやつ

- クライアント側：誰に挨拶するかを決める
- サーバ側：挨拶するフレーズを生成

### 1. 必要なツールやライブラリをインストールする

```
npm init

npm install grpc @grpc/proto-loader
or
npm install @grpc/grpc-js @grpc/proto-loader
```

grpcパッケージじゃなく下のgrpc-jsの方がいいかも？
[参考](https://grpc.io/blog/grpc-js-1.0/)

### 2. Protocol Buffersを使って定義ファイルを書く
```helloworld.proto
// Protocol Buffersのバージョンを指定
syntax = "proto3";

// パッケージを指定
package helloworld;

// サービスを定義
service Greeter {
  // 引数の型：HelloRequestメッセージ
  // 戻り値の型：HelloReplyメッセージ
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// メッセージの型を定義
message HelloRequest {
  // 各フィールドに識別子としてタグを付ける
  // 値をフィールドに代入しているわけではないことに注意
  string name = 1;
}

message HelloReply {
  string message = 1;
}
```

### 3. (定義ファイルをコンパイルしてコードを生成する)
node.jsはコンパイルをせず2.で作成した定義ファイルを読み込めばヨシ！！

### 4. 定義に沿ってサーバとクライアントを実装する
**サーバ側**

クライアントからアクセスがあったら挨拶文を生成する(sayHelloする)サーバを動かしたい

```server.js
const protoLoader = require('@grpc/proto-loader');
// インストールしたパッケージに合わせる
// const grpc = require('grpc');
const grpc = require('@grpc/grpc-js');
const PROTO_PATH = __dirname + '/helloworld.proto'

// 定義ファイル(.protoファイル)の読み込み
const packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;

/**
 * sayHelloメソッド
 */
function sayHello(call, callback) {
  callback(null, {message: 'Hello ' + call.request.name});
}

/**
 * mainメソッド
 */
function main() {
  // サーバのインスタンスを生成
  const server = new grpc.Server();
  // サーバがGreeterサービスのリクエストを受け取るようにする
  server.addService(hello_proto.Greeter.service, {sayHello: sayHello});
  // クライアントのリクエストをリッスンする(外部からのアクセスに備えて待機する)ためのアドレスとポートを指定
  server.bindAsync('127.0.0.1:50051', grpc.ServerCredentials.createInsecure(), () => {
    // サーバを起動する
    server.start();
  });
}

main();
```

サーバを起動！

```
node server.js
```

**クライアント側**
クライアントがやりたいのは

- 誰に挨拶をするか決める
- サーバ側(Remote)のsayHello(Procedure)を呼び出し(Call)

```client.js
const PROTO_PATH = __dirname + '/helloworld.proto';

// インストールしたパッケージに合わせる
// const grpc = require('grpc');
const grpc = require('@grpc/grpc-js');
// 定義ファイル(.protoファイル)の読み込み
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;

/**
 * mainメソッド
 */
function main() {
  // サーバのアドレスとポートを指定してGreeterサービスのスタブコンストラクタを呼び出す
  const client = new hello_proto.Greeter('127.0.0.1:50051',
                                       grpc.credentials.createInsecure());
  // スタブでsayHelloメソッドの呼び出し
  // リクエスト(HelloRequestメッセージ型のオブジェクト)とコールバック関数をサーバ側に渡す
  client.sayHello({name: 'World'}, function(err, response) {
    console.log('Greeting:', response.message);
  });
}

main();

```
クライアントを起動！

```
node server.js
```

``Greeting:Hello World``と出力されれば成功です！！

お疲れ様でした！！

## 最後に
初めての勉強会開催だったので皆さんの感想が気になります！！
[#xhack勉強会](https://twitter.com/search?q=%23xhack%E5%8B%89%E5%BC%B7%E4%BC%9A&src=typeahead_click) でツイートしてくれたら嬉しいです！

https://x-hack.connpass.com/

**近日開催されるxhack勉強会**

- [「デジタル・マーケティングの定石を学ぶ」著：垣内勇威 読書会 vol.2](https://x-hack.connpass.com/event/207093/)
- [【アフィリエイトについて語ろう】検索エンジンを再発明する Meet Up vol.01](https://x-hack.connpass.com/event/206029/)
- [みんなでUIを改善してみるUIデザイン勉強会](https://x-hack.connpass.com/event/208144/)


### 株式会社マンハッタンコード
現在私が所属している株式会社マンハッタンコードでは、スマホアプリ開発を主軸にさまざまな挑戦をしています。
毎週土曜日にはイベントを開催しているので、気軽に遊びに来てください！

https://www.mht-code.com/

https://mht-code.connpass.com/

--- 
最後に、今回の勉強会を開く後押しをしてくださり、当日も参加してサポートしてくださったX-HACKの[松田さん](https://twitter.com/xhackjp1)、[とよももさん](https://twitter.com/momokotoyopet)、そして日曜の夜にわざわざ時間を作って参加してくださった皆さん、ありがとうございました！


## 参考
- スターティングgRPC(https://booth.pm/ja/items/1315322)
- よくわかるgPRC(https://booth.pm/ja/items/1557285)
- https://noumenon-th.net/programming/2019/07/05/grpc/