👀

【入門】コピペでできる、Node.js(NestJS) + gRPCでサーバー構築

2023/03/03に公開

はじめに

個人的にはgRPCの学習コストは結構高く、技術記事を見ても途中で上手くいかず過去困ることがありました。

なので、gRPCサーバー立てて、ターミナルのクライアントからのアクセスをコピペでサクッとできる!をゴールに記事を書きますので、最初の足掛かりとしていただけたらと思います。

なお、NestJSを使ったのはただの好みです。

環境

タイトル バージョン
Node.js v18.14.2
npm 9.5.0

全体の流れ

  1. 初期構築
  2. 設定ファイル書き換え
  3. protoファイル作成
  4. protoファイルからtsファイル生成
  5. gRPCサーバーの実装
  6. クライアントツールのEvansからgRPCサーバーにアクセス

初期構築

まずはじめに、アプリケーション開発を支援してくれるNest CLIをインストールし、NestJSプロジェクトを作成します。

npm or yarn or pnpm のどれにするか聞かれますが、今回はnpmを選択します。

ターミナル
$ npm i -g @nestjs/cli
$ nest new project-name
$ cd project-name

protoファイルからTypeScriptの型定義を生成してくれて、かつNestJSをガッチリサポートしているts-protoを入れます。

また、gRPCサーバー構築に必要なパッケージをインストールします。

ターミナル
$ npm install ts-proto
$ npm install @grpc/grpc-js @grpc/proto-loader @nestjs/microservices

Nest CLIでひな形のファイルを生成しておきましょう。

ターミナル
$ nest g controller hero
$ nest g module hero
$ nest g service hero

ひとまずサーバー起動することを確認します。

ターミナル
$ npm start

ブラウザにhttp://localhost:3000と入力して、Hello World!となれば確認OKです。

nest-cli.jsonの書き換え

Nestの設定ファイルを書き換えます。

compilerOptionsassetswatchAssetsを指定します。

コンパイルすると通常tsファイル以外はdistに配置してくれませんが、assetsに書くと指定したファイルを持っていってくれるようになります。

nest-cli.json
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": ["**/*.proto"],
    "watchAssets": true
  }
}

protoファイル作成

src/heroディレクトリにprotoファイルを新規作成します。

src/hero/hero.proto
// hero/hero.proto
syntax = "proto3";

package hero;

service HeroesService {
  rpc FindOne (HeroById) returns (Hero) {}
}

message HeroById {
  int32 id = 1;
}

message Hero {
  int32 id = 1;
  string name = 2;
}

protoファイルからtsファイルを生成

下記コマンドを実行してみましょう。hero.tsが生成されます。

ターミナル
protoc --ts_proto_opt=nestJs=true --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./src/hero/hero.proto

main.tsの編集

src/main.tsを編集します。

ポートの指定ないけどいいの?と思うかもしれませんが、この構築方法だとデフォルトが5000番ポートに変更されます。

src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.GRPC,
      options: {
        package: 'hero',
        protoPath: join(__dirname, 'hero/hero.proto'),
      },
    },
  );
  await app.listen();
}
bootstrap();

hero.controller.tsの編集

src/hero/hero.controller.tsを編集します。

@GrpcMethodにprotoファイルで指定したサービス名を定義しています。

src/hero/hero.controller.ts
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { HeroById, Hero } from './hero';

@Controller()
export class HeroesController {
  @GrpcMethod('HeroesService', 'FindOne')
  findOne(data: HeroById): Hero {
    const items = [
      { id: 1, name: 'John' },
      { id: 2, name: 'Doe' },
    ];
    return items.find(({ id }) => id === data.id);
  }
}

hero.module.tsの編集

src/hero/hero.module.tsを編集します。

src/hero/hero.module.ts
import { Module } from '@nestjs/common';
import { HeroesController } from './hero.controller';
import { HeroService } from './hero.service';

@Module({
  providers: [HeroService],
  controllers: [HeroesController],
})
export class HeroModule {}

hero.service.tsの編集

src/hero/hero.service.tsを編集します。(多分編集不要)

src/hero/hero.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class HeroService {}

app.module.tsの編集

src/app.module.tsを編集します。

src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HeroesController } from './hero/hero.controller';
import { HeroModule } from './hero/hero.module';

@Module({
  imports: [HeroModule],
  controllers: [AppController, HeroesController],
  providers: [AppService],
})
export class AppModule {}

サーバー起動🚀

npm startでサーバー起動します。

Evansで確認

EvansというgRPCのクライアントツールがあるのでそこから確認してみましょう。

別のターミナルを開き、まずはEvansをインストールします。

ターミナル
brew tap ktr0731/evans
brew install evans

ターミナルから今回のプロジェクトルートまで移動してコマンドを実行します。

ターミナル
evans --host localhost -p 5000 src/hero/hero.proto

下記の画面になれば成功です。

show serviceコマンドで情報が確認できます。

ターミナルにcall FindOneと打って、その後1を入力します。

わーい!返ってきたぞーい!!

ちなみにEvansはCmd + Dで終了になります。

さいごに

リポジトリは下記にあります。

https://github.com/hiroaki-saito/hands-on-nestjs-grpc

参考

https://engineering.mercari.com/blog/entry/20201216-53796c2494/

https://docs.nestjs.com/microservices/grpc

https://github.com/ktr0731/evans#macos

Discussion