Microservices — also known as the microservice architecture — is an architectural style that structures an application as a collection of services that are:
- Independently deployable
- Loosely coupled
- Organized around business capabilities
- Owned by a small team
The microservice architecture enables an organization to deliver large, complex applications rapidly, frequently, reliably, and sustainably — a necessity for competing and winning in today’s world.
NestJS is a framework for building efficient, scalable NodeJS server-side applications. In addition to monolithic application architecture, Nest natively supports the microservice architectural development style. In Nest, a microservice is fundamentally an application that uses a different transport layer than HTTP.
Nest supports six built-in transport layer implementations, called transporters, which are responsible for transmitting messages between different microservice instances.
Most transporters natively support both request-response and event-based message styles. In this article, we will use gRPC as the transporter.
gRPC is a modern, open-source, high-performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking, and authentication. You can read more about it here.
Please ensure that Node.js (version >= 16) is installed on your operating system.
To start building gRPC-based microservices, first, install the required packages:
$ npm i -g @nestjs/cli $ npm i --save @nestjs/microservices $ npm i --save @grpc/grpc-js @grpc/proto-loader
$ nest new http-api $ nest new user-service
http-api will expose http endpoints for clients. user-service doesn’t expose any http endpoints instead, it exposes a URL through which http-api can communicate using gRPC. The idea is http-api act as an API gateway and user-service holds the business logic. Later, we can add more similar kinds of services.
Also, we create a separate folder called protowhere we keep all the .proto files and access them in both applications.
First, in the nest-cli.json file of both applications, we add the assets property that allows us to distribute non-TypeScript files, and watchAssets - to turn on watching all non-TypeScript assets. In our case, we want .proto files to be automatically copied to the dist folder.
Sample gRPC service
Like many RPC systems, gRPC is based on the concept of defining a service in terms of functions (methods) that can be called remotely. For each method, we define the parameters and return types. Services, parameters, and return types are defined in .proto files using Google's open-source language-neutral protocol buffers mechanism.
With the gRPC transporter, Nest uses .proto files to dynamically bind clients and servers to make it easy to implement remote procedure calls, automatically serializing and deserializing structured data.
Let’s define our sample gRPC service called UserService. When we implement the createMicroservice method in user-service application, theprotoPath property sets a path to the .proto definitions file user.proto. The user.proto file is structured using protocol buffers. Here's what it looks like:
Ours UserService exposes a getUsers() method. This method expects an input argument of type getUserRequest and returns a getUserResponse message (protocol buffers use message elements to define both parameter types and return types).
Configure the user-service application
To use gRPC in Nest application we select the gRPC transporter mechanism using the transport property of the GrpcOptions object passed to the createMicroservice() method. The options property provides metadata about that service.
Next, we will create a new nest module to implement the service.
$ nest g module modules/users $ nest g co /modules/users
To define a handler that fulfills this definition, we use the @GrpcMethod() decorator in the newly created controller, as shown below. This decorator provides the metadata needed to declare a method as a gRPC service method.
The decorator shown above takes two arguments. The first is the service name (e.g., 'UserService'), corresponding to the UserService service definition in user.proto. The second (the string 'getUsers') corresponds to the getUsers() rpc method defined within UserService in the user.proto file.
Configure the http-api application
First, we will add a new nest module in http-api
$ nest g module modules/protousers $ nest g co /modules/protousers
This module will act as a gRPC client by consuming services defined in .proto files as well as expose protouser endpoint. To access remote services we have to use an ClientGrpc object. The preferred way to get ClientGrpc object is to import ClientsModule and use the register() method to bind a package of services defined in a .proto file to an injection token, and to configure the service. The name property is the injection token. For gRPC services, use transport: Transport.GRPC.
Once registered, we can inject the configured ClientGrpc object with @Inject(). Then we use the ClientGrpc object's getService() method to retrieve the service instance, as shown below.
Run the applications
$ cd http-api && npm start:dev $ cd user-service && npm start:dev
Now, we can call the http://localhost:3000/protousers endpoint of http-apiwhich will internally communicate with user-service and return the data as response. In this way, we can use gRPC to communicate between different services. All the code used here with another service(book-service) you can find at https://github.com/sourav15/nestjs-grpc-microservice.