10 Oct 2023 · Software Engineering

    Microservices Communication in NestJS With gRPC

    11 min read
    Contents

    Imagine you go home for the holidays, and your family decides to have a gathering. Instead of having one person do all the cooking, everyone agrees to bring a specialized dish they are good at making. The goal is to create a complete meal by combining all these dishes. You agree to make the chicken fried rice, your brother the cake for dessert, and your sister the salad.

    Each dish represents a microservice with a specific function or purpose. For example, the fried rice represents a hearty main dish; the salad represents a fresh and healthy side dish, and the cake represents a sweet dessert. In a microservices architecture, developers can develop, deploy, and maintain each microservice independently from the others, ensuring that each microservice remains independent and self-contained.

    In the same way, that each dish at a family gathering represents a microservice with a distinct function or purpose, each microservice in a software system must communicate efficiently with others to form a fully functioning application. Similar to how each dish needs to be coordinated with others to create a good and enjoyable meal, communication between microservices becomes gradually more crucial as the number of them rises. We can ensure that microservices can function seamlessly to build a durable and scalable system by using efficient communication frameworks like gRPC.

    In this article, we’ll look at how gRPC, a high-performance, open-source remote procedure call framework, allows microservices in a NestJS application to communicate. We’ll go through the advantages of using gRPC, how to do so in a NestJS application, and give an example of how microservices might communicate using it.

    Prerequisites

    Let’s get started by going over the prerequisites and tools we’ll need:

    • Node.js with NPM – To run and handle the dependencies for our application.
    • NestJS CLI – For building, managing, and running NestJS applications.
    • Protocol Buffers – To define messages and services in gRPC.
    • gRPC – To allow microservices in your NestJS application to communicate with one another.
    • grpcurl – To allow you to test your simple microservice communication.

    Microservices and How They Communicate

    Microservices communicate with each other in a variety of ways. As each microservice in a microservices architecture performs a specific function or task, communication is necessary to ensure the full application functions properly. Microservices architectures enable developers to create more flexible, scalable, and fault-tolerant applications due to the independent nature of each microservice. Additionally, microservices’ modular design makes them ideal for complex applications, as different system components may have varying demands and requirements.

    Challenges of Microservices Communication

    Microservices often communicate with one another via well-defined APIs and protocols, allowing each microservice to do so reliably and efficiently. However, inter-microservice communication can be a little challenging and tricky. Developers could run into several problems when many microservices communicate with one another, including:

    • Latency: caused by network congestion due to inefficient communication and data transfer.
    • Security: potential vulnerabilities and threats could result from poor access control management or not bearing the necessary security protocols.
    • Service Discovery: with an increase in the number of services in a system, it becomes difficult to manage and locate the appropriate service. Even worse, having hard-coded endpoints can lead to brittle systems.
    • Fault Tolerance: With multiple services interacting with each other, any service failure can have a rippling effect on the entire system.

    Communication across microservices can be challenging and complex overall. Developers can create highly scalable, fault-tolerant, and resilient microservices architectures by designing the system with the following in mind:

    • Communication in mind.
    • Efficient communication protocols.
    • Proper security protocols.
    • Service discovery mechanisms to dynamically locate and communicate with services.

    Communication Patterns in Microservices

    Microservices communicate with each other using various communication patterns. We will look at some of these: publish-subscribe, request-response, event-driven architecture, and message queuing.

    • Publish-Subscribe Pattern: Involves one-to-many communication, where it publishes a message to multiple subscribers. For instance, a company sends out emails to its various newsletter subscribers.
    • Request-Response Pattern: One service sends a request to another service, and the recipient service sends back a response. For instance, when an API receives a request and sends back the requested data or an error message in response.
    • Message Queuing Pattern: In this pattern, messages are sent to a queue and stored until a service is available to process them. For example, a delivery company may use this pattern to receive and organize customer delivery requests and assign drivers as they become available based on their location.
    • Event-Driven Architecture: In this pattern, services exchange messages when events occur. For example, when a user sends money from their mobile banking app, the account update service receives a notification to deduct the amount.

    An Overview of gRPC

    gRPC is a high-performance, open-source remote procedure call (RPC) framework developed by Google. It enables a client application to invoke a method on a server application located on a remote machine with the same ease as calling a method on a local object. It simplifies the process of building and scaling distributed applications by providing efficient and language-independent communication between client and server applications.

    gRPC vs. REST vs. SOAP in Microservices

    Communication protocols used in microservices architecture include gRPC, REST, and SOAP. Here are some key differences between them:

    • Language support: REST and SOAP are commonly used with web-based programming languages, while gRPC supports a wide range of programming languages, including C++, Java, Python, and more.
    • Performance: Because gRPC uses binary serialization, a compressed data format, and bidirectional streaming, it is quicker and more effective than REST and SOAP. As a result, client and server applications can communicate in real-time.
    • Data format: REST and SOAP use XML or JSON, whereas gRPC uses Protocol Buffers, a binary serialization standard.
    • Strong typing and service contracts: To establish service contracts, gRPC uses Protocol Buffers, which offer strong typing and aid in service versioning and maintenance. REST and SOAP use less expressive and flexible WSDL or OpenAPI definitions for their service contracts.
    • Scalability: gRPC is a superior option for microservices architecture because it is built to handle large-scale distributed systems and comes with capabilities like load balancing and health checking out of the box.

    Using gRPC in NestJS

    In this section you are going to learn how to create a simple NestJS microservice that returns “Hello, World!” using gRPC:

    • First, you need to install NestJS CLI and gRPC tools:
    npm install -g @nestjs/cli
    npm install @nestjs/microservices @grpc/grpc-js
    • You need to create a new NestJS project using the CLI:
    nest new hello-world-demo

    The name of the project in this example is “hello-world-demo.”

    • Now, navigate into the project folder:
    cd hello-world-demo
    • Next, you need to create a new module and service. A service is a class that offers a certain type of functionality to the application, whereas a module is a container for a collection of related services, controllers, and providers. By doing this, you are basically adding a new layer of manageable, testable functionality to your NestJS application. This could help the addition of additional features in the future and benefit your application’s general architecture and maintainability.
    nest g module hello
    nest g service hello
    • Then you need to update the hello-world-demo/src/hello/hello.service.ts file with the following code:
    import { Injectable } from '@nestjs/common';
    import { GrpcMethod } from '@nestjs/microservices';
    
    @Injectable()
    export class HelloService {
      @GrpcMethod('HelloService', 'SayHello')
      sayHello(data: any): { message: string } {
        return { message: 'Hello, World!' };
      }
    }

    This service file contains a HelloService class that is marked as an injectable provider. The @GrpcMethod decorator is used to mark the sayHello method as a gRPC method, which returns a “Hello, World!” message. Overall, this code demonstrates how to create a basic gRPC service using NestJS.

    • Update the hello-world-demo/src/app.module.ts file to include the HelloService and to use gRPC:
    import { Module } from '@nestjs/common';
    import { ClientsModule, Transport } from '@nestjs/microservices';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { HelloService } from './hello/hello.service';
    
    @Module({
      imports: [
        ClientsModule.register([
          {
            name: 'HELLO_PACKAGE',
            transport: Transport.GRPC,
            options: {
              url: 'localhost:5000',
              package: 'hello',
              protoPath: './hello.proto',
            },
          },
        ]),
      ],
      controllers: [AppController],
      providers: [AppService, HelloService],
    })
    export class AppModule {}

    It has a gRPC client for the HelloService, a HelloService provider class, an HTTP controller, and AppService and HelloService provider classes. The ClientsModule is used to register the gRPC client with the supplied options, such as the path to the proto file that describes the service, the URL of the gRPC server, and the name of the package. The AppService and HelloService classes, which manage business logic in the application, are included in the providers field. The AppController class, which manages incoming HTTP requests, is a member of the controllers property.

    • Create a new file hello-world-demo/hello.proto and add the following code:
    syntax = "proto3";
    
    package hello;o
    
    service HelloService {
      rpc SayHello (HelloRequest) returns (HelloResponse);
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloResponse {
      string message = 1;
    }

    The proto file is used to define the structure and interface of the gRPC service. It is a file that outlines the service’s procedures as well as the parameters’ input and output data formats. The gRPC framework uses the proto file, which is written in the Protocol Buffer language, to produce client and server code in NestJs.

    • Finally, update the hello-world-demo/src/main.ts file to start the gRPC microservice:
    import { NestFactory } from '@nestjs/core';
    import { Transport } from '@nestjs/microservices';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.createMicroservice(AppModule, {
        transport: Transport.GRPC,
        options: {
          url: 'localhost:5000',
          package: 'hello',
          protoPath: './hello.proto',
        },
      });
      await app.listen();
    }
    bootstrap();
    • Now, start the gRPC microservice:
    npm run start

    Once the server has started, it will be listening for incoming gRPC requests on the specified URL and port, which in this case is 5000. You can then send requests to the server from a gRPC client, such as a web frontend, mobile app, or another microservice. In the case of the “Hello World” example we’ve been working on, you can test the server by sending a request using the grpcurl command-line tool. To test the microservice, you can use grpcurl, which is a command-line tool for interacting with gRPC services:

    grpcurl -plaintext -d '{"name": "John"}' localhost:5000 hello.HelloService/SayHello

    This should return the following response:

    {
      "message": "Hello, World!"
    }

    That’s it! You’ve created a NestJS microservice that returns “Hello, World!” using gRPC, and you’ve tested it using grpcurl. Of course, in a real-world application, your microservice will likely have more complex functionality than simply returning “Hello, World!” in response to a single request. But the basic process of starting the server, listening for incoming requests, and handling those requests according to your business logic will be the same.

    Benefits of using gRPC for Microservices Communication

    Using NestJS and gRPC for microservices communication can result in faster, more reliable, and more scalable systems. Here are a few benefits:

    • Strongly typed contracts: The API provided by gRPC, which employs Protocol Buffers to construct service contracts, is strongly typed. In other words, both the server and the client can make sure that the messages they send and receive follow a particular schema.
    • Automatic code generation: NestJS offers tools that make it simple to automatically generate TypeScript code from your gRPC service definition file (.proto), which makes integrating your microservices with NestJS simple.
    • Interoperability: makes it simpler to combine NestJS and gRPC with other services that make use of different communication protocols by allowing them to function with other protocols like HTTP/REST.
    • Ease of use: NestJS offers an easy-to-understand API for defining gRPC services, making it simple to build and manage microservices.

    Conclusion

    Microservices architecture is becoming a more popular way of designing complex and scalable applications. It offers flexibility and scalability by breaking down applications into independent and specialized components. However, the effectiveness of this design depends on efficient communication between microservices. This is where gRPC, a high-performance remote procedure call (RPC) framework, comes into play.

    In this article, we explored the benefits of using gRPC for microservices communication in NestJS. We discussed the challenges involved in microservices communication, including latency, security, service discovery, and fault tolerance. We also explored how gRPC can help developers overcome these challenges and build scalable, fault-tolerant microservices architectures. Additionally, we compared gRPC with other communication protocols like REST and SOAP, highlighting its language support, performance, data format, and service contract advantages.

    Furthermore, we built a simple “hello world” service using gRPC in NestJS to demonstrate how services in a Nest application can effectively communicate using gRPC.

    Finally, we discussed the benefits of using gRPC for microservices communication in NestJS, including strongly typed contracts, automatic code generation, interoperability, and ease of use.

    In summary, developers can build scalable and maintainable microservices with effective communication channels using the performance and type-safety of gRPC and the simplicity of NestJS, leading to more robust and reliable systems.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by:
    Oghenevwede Emeni is a software developer with over 6 years of experience. Oghenevwede runs her own ecommerce startup and loves writing during her free time!
    Avatar
    Reviewed by:
    I picked up most of my soft/hardware troubleshooting skills in the US Army. A decade of Java development drove me to operations, scaling infrastructure to cope with the thundering herd. Engineering coach and CTO of Teleclinic.