Handling gRPC services with Nest.js
In Nest.js, we have some transport layer implementations that are prepared for microservices. One of them is the gRPC transporter which is definitely one of the most interesting. In this article, we’ll explore the idea behind this layer and how we can implement it in NestJS.
Creation of a project model
$ node -v
v10.14.1
$ yarn global add @nestjs/cli
Install the necessary packages
$ yarn add @nestjs/microservices @grpc/proto-loader grpc protobufjs
This time I created a project with Nestjs CLI and defined a module called module rest.
$ nest new nestjs-grpc-client
$ nest g mo rest
With let’s change the file main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3001);
}
bootstrap();
Let’s now create the .proto file’s job is to describe the service and the methods we want to use remotely.
syntax = "proto3";
import "rpc/champion.proto";
package rpc;
service Rpc {
rpc GetChampion (GetChampionRequest) returns (GetChampionResponse);
rpc ListChampions (Empty) returns (ListChampionsResponse);
}
message Empty {}
message GetChampionRequest {
int32 champion_id = 1;
}
message GetChampionResponse {
champion.Champion champion = 1;
}
message ListChampionsResponse {
repeated champion.Champion champions = 1;
}
///protos/rpc/champion.proto
syntax = "proto3";
package champion;
message Champion {
enum ChampionType {
UNKNOWN = 0;
FIGHTER = 1;
TANK = 2;
SUPPORT = 3;
}
int32 champion_id = 1;
ChampionType type = 2;
string name = 3;
string message = 4;
}
A controller that provides RPC server functionality as a REST API
Antes de escrever nosso controller vamos configurar as opções que serão passada para o decorator @Client
que será importado da biblioteca @nestjs/microservices.
import { ClientOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
const protoDir = join(__dirname, '..', 'protos');
export const grpcClientOptions: ClientOptions = {
transport: Transport.GRPC,
options: {
url: '0.0.0.0:5000',
package: 'rpc',
protoPath: '/rpc/rpc.proto',
loader: {
keepCase: true,
longs: Number,
enums: String,
defaults: false,
arrays: true,
objects: true,
includeDirs: [protoDir],
},
},
};
In this example, we assume a use case where another gRPC server is called a web server built with Nest.js, and its function is provided as a REST API.
Next is the part that calls the function of the service that manages the Controller’s gRPC. The gRPC client is acquired by Decorator @Client
the service actually used is acquired dynamically at the time of the lifecycle hook onModuleInit
.
When I tried to get the service in Builder without using this lifecycle hook, an error occurred, so it seems better to use it silently.
import { Controller, Get, Query, OnModuleInit } from '@nestjs/common';
import { Client, ClientGrpc } from '@nestjs/microservices';
import {
RpcService,
Empty,
GetChampionResponse,
GetChampionRequest,
ListChampionsResponse,
} from '../../types';
import { grpcClientOptions } from '../grpc-client.options';
@Controller('rest')
export class RestController implements OnModuleInit {
@Client(grpcClientOptions) private readonly client: ClientGrpc;
private rpcService: RpcService
onModuleInit(): void {
this.rpcService = this.client.getService<RpcService>('Rpc');
}
@Get('champion')
async getChampion(
@Query() request: GetChampionRequest,
): Promise<GetChampionResponse> {
return this.rpcService.getChampion(request);
}
@Get('champions')
async getChampions(@Query() request: Empty): Promise<ListChampionsResponse> {
return this.rpcService.listChampions(request);
}
}
In the real use situation, a pattern that responds after processing the information acquired from the gRPC server on the REST server-side is assumed.
In this case, I think it will be done in the service layer instead of dealing with the gRPC client directly in the controller like this time.
You can see that gRPC server content can be called a REST API as follows.
...{
"champion": {
"champion_id": 1,
"type": 1,
"name": "Felipe Marques",
"message": "uhuuuuu you are champion"
}
}...
Conclusion
The Nest.js microservices
package itself still looks underdeveloped (the interfaces are defined but not implemented), I welcome gRPC microservice development with Nest.js so I would like to continue using it in the future.
Comments