— NestJS, Microservices, gRPC — 2 min read
In this blog post, I'm going to create a gRPC microservice using NestJS.
gRPC is an open source Remote Procedure Call developed by Google in 2015. It uses HTTP/2 for transport and Protocol Buffers to encode the data, which have a stricter specification compared with JSON. It is language-neutral and platform-neutral so it can run in any environment.
Before you begin, make sure that you have NestJS installed on your machine.
We will start by creating 2 different services.
The first service that we're going to create is Hello World Service
. This is just going to be a
simple service that returns Hello World!
upon called.
Create a new NestJS project called hello-world-service
.
$ nest new hello-world-service
Install the required packages.
$ npm i @nestjs/microservices grpc @grpc/proto-loader
Create a .proto
file to define your protobuf.
1syntax = "proto3";23package hello;45service HelloWorldService {6 rpc GetHelloWorld (HelloWorldReq) returns (HelloWorld) {}7}89message HelloWorldReq {}1011message HelloWorld {12 string reply = 1;13}
Update your main.ts
file to this:
1import { Logger } from '@nestjs/common';2import { NestFactory } from '@nestjs/core';3import { MicroserviceOptions, Transport } from '@nestjs/microservices';4import { join } from 'path';5import { AppModule } from './app.module';67const logger = new Logger();89async function bootstrap() {10 const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {11 transport: Transport.GRPC,12 options: {13 package: 'hello',14 protoPath: join(__dirname, './protos/hello-world.proto'),15 },16 });17 await app.listen(() => logger.log('Hello World service is listening...'));18}19bootstrap();
Update your app.controller.ts
to this:
1import { Controller } from '@nestjs/common';2import { GrpcMethod } from '@nestjs/microservices';34@Controller()5export class AppController {6 @GrpcMethod('HelloWorldService', 'GetHelloWorld')7 getHelloWorld() {8 return { reply: 'Hello World!'};9 }10}
Note that we're using @GrpcMethod()
decorator in our controller to tell NestJS that this method
is a gRPC service method. The first argument in the decorator denotes our service name which we
defined in our hello-world.proto
file while the second argument denotes our RPC method within
HelloWorldService
in the hello-world.proto
file.
The second service that we're going to create is Custom Hello Service
. This is just going to be a
simple service that accept a string as params
and returns Hello <params>!
upon called. We will
repeat several steps from the Hello World Service
above.
Create a new NestJS project called custom-hello-service
.
$ nest new custom-hello-service
Install the required packages.
$ npm i @nestjs/microservices grpc @grpc/proto-loader
Create a .proto
file to define your protobuf.
1syntax = "proto3";23package customHello;45service CustomHelloService {6 rpc GetCustomHello (CustomHelloReq) returns (CustomHello) {}7}89message CustomHelloReq {10 string data = 1;11}1213message CustomHello {14 string reply = 1;15}
Update your main.ts
file to this:
1import { Logger } from '@nestjs/common';2import { NestFactory } from '@nestjs/core';3import { MicroserviceOptions, Transport } from '@nestjs/microservices';4import { join } from 'path';5import { AppModule } from './app.module';67const logger = new Logger();89async function bootstrap() {10 const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {11 transport: Transport.GRPC,12 options: {13 package: 'customHello',14 protoPath: join(__dirname, './protos/custom-hello.proto'),15 },16 });17 await app.listen(() => logger.log('Custom Hello service is listening...'));18}19bootstrap();
Update your app.controller.ts
to this:
1import { Controller } from '@nestjs/common';2import { GrpcMethod } from '@nestjs/microservices';34@Controller()5export class AppController {6 @GrpcMethod('CustomHelloService', 'GetCustomHello')7 getCustomHello(data: string) {8 return { reply: `Hello ${data}!` };9 }10}
Same as the previous Hello World Service
, the first parameter in @GrpcMethod()
decorator
denotes our service name which we defined in our custom-hello.proto
file while the second
argument denotes our RPC method within CustomHelloService
in the custom-hello.proto
file.
API gateway is a server that act as the single entry point to our system. It encapsulates internal system architecture and provides an API that is tailored to each client. Basically, API gateway is the one that is going to expose our microservices to the outside world.
Create a new NestJS project called api-gateway
.
$ nest new api-gateway
Install the required packages.
$ npm i @nestjs/microservices grpc @grpc/proto-loader
Copy the .proto
files from the previous services into src/protos
folder.
Update your app.controller.ts
file to this:
1import { Controller, Get, Param, Query } from '@nestjs/common';2import { AppService } from './app.service';34@Controller()5export class AppController {6 constructor(private readonly appService: AppService) {}78 @Get('hello-world')9 getHelloWorld() {10 return this.appService.getHelloWorld();11 }1213 @Get('custom-hello')14 getCustomHello(@Query('q') q: string) {15 return this.appService.getCustomHello(q);16 }17}
Update your app.service.ts
file to this:
1import { Injectable, OnModuleInit, Inject } from '@nestjs/common';2import { ClientGrpc } from '@nestjs/microservices';3import { Observable } from 'rxjs';45interface HelloWorldService {6 getHelloWorld(data: any): Observable<any>;7}89interface CustomHelloService {10 getCustomHello(data: any): Observable<any>;11}1213@Injectable()14export class AppService implements OnModuleInit {15 private helloWorldService: HelloWorldService;16 private customHelloService: CustomHelloService;1718 constructor(19 @Inject('HELLO_WORLD_SERVICE') private helloWorldClient: ClientGrpc,20 @Inject('CUSTOM_HELLO_SERVICE') private customHelloClient: ClientGrpc21 ) {}2223 onModuleInit() {24 this.helloWorldService = this.helloWorldClient.getService<HelloWorldService>('HelloWorldService');25 this.customHelloService = this.customHelloClient.getService<CustomHelloService>('CustomHelloService');26 }2728 getHelloWorld() {29 return this.helloWorldService.getHelloWorld({});30 }3132 getCustomHello(data: string) {33 return this.customHelloService.getCustomHello({ data });34 }35}
Lastly, update your app.module.ts
file to this:
1import { Module } from '@nestjs/common';2import { ClientsModule, Transport } from '@nestjs/microservices';3import { join } from 'path';4import { AppController } from './app.controller';5import { AppService } from './app.service';67@Module({8 imports: [ClientsModule.register([9 {10 name: 'HELLO_WORLD_SERVICE',11 transport: Transport.GRPC,12 options: {13 package: 'hello',14 protoPath: join(__dirname, './protos/hello-world.proto'),15 },16 },17 {18 name: 'CUSTOM_HELLO_SERVICE',19 transport: Transport.GRPC,20 options: {21 package: 'customHello',22 protoPath: join(__dirname, './protos/custom-hello.proto'),23 },24 },25 ]),],26 controllers: [AppController],27 providers: [AppService],28})29export class AppModule {}
To test the microservices, you can use Postman or cURL
and do a GET
method to localhost:3000/hello-world
to test the Hello World Service
and
localhost:3000/custom-hello?q={query}
to test the Custom Hello Service
.
You can see the full source code here.