Table of Contents


Implementing Microservices in NestJS using gRPC

Introduction

In this section, we will rebuild the same NestJS microservices architecture, using gRPC. gRPC is a high-performance, open-source RPC framework that enables fast and efficient request–response communication between microservices using Protocol Buffers (Protobuf) for data serialization.

By using gRPC, microservices communicate through strongly defined contracts, ensuring type safety and consistency across services. This approach is especially well suited for synchronous service-to-service communication in modern distributed systems where low latency and reliability are critical.

What is gRPC?

gRPC is a communication framework that allows one service to call a function of another service as if it were a local method call. It uses Protocol Buffers (proto files) to define the structure of requests and responses and runs on HTTP/2, making it fast and efficient for service-to-service communication.

Why Use gRPC in Microservices?

What We Will Build (gRPC Version)

In this section, we will create four NestJS microservices that communicate using gRPC.

Microservices Overview

User Service – Manages user-related operations

Order Service – Handles order creation and management

Payment Service – Processes payments and transactions

API Gateway – Acts as a single HTTP entry point for client requests

Communication Layer

gRPC will serve as the primary communication protocol between services, enabling fast, synchronous, and strongly typed request–response communication.

This setup demonstrates a high-performance microservices architecture using NestJS and gRPC, ideal for internal service-to-service communication.

Architecture Overview (gRPC)

This architecture demonstrates how NestJS microservices communicate using gRPC with a centralized API Gateway.

High-Level Flow

  1. The client (browser) sends an HTTP request to the API Gateway.
  2. The API Gateway acts as the single entry point for all client requests.
  3. The API Gateway forwards the request to the appropriate microservice using gRPC.
  4. User Service, Order Service, and Payment Service process the request and return a gRPC response back to the API Gateway.
  5. The API Gateway sends the final HTTP response to the client.

Architecture Diagram

Key Differences: Kafka vs gRPC

Both Kafka and gRPC are widely used in microservices architectures, but they serve different purposes and communication patterns.

Kafka

  • Event-based and asynchronous communication
  • Uses a message broker (Kafka broker)
  • Best suited for event-driven architectures
  • Supports message replay and loose coupling
  • Highly scalable and fault-tolerant

gRPC

  • Synchronous request–response communication
  • Direct service-to-service communication
  • Best suited for API-based communication
  • Enforces strong contracts with Protobuf
  • Low-latency and high performance

When to Use What?

Use Kafka for events, workf lows, and background processing

Use gRPC for real-time APIs and synchronous service calls

In real-world systems, Kafka and gRPC are often used together to build scalable and efficient microservices.

Prerequisites

Node.js installed

NestJS CLI installed

Basic understanding of microservices

Step 1: Install NestJS CLI


npm i -g @nestjs/cli					

Choose npm or yarn and enable TypeScript.

Step 2: Create NestJS Applications

  1. 3 Microservices
  2. API Gateway – Client

#1 nest new user-service

#2 nest new order-service

#3 nest new payment-service

#4 nest new api-gateway

Each service runs independently – this is the core idea of microservices.

Step 3: Install gRPC Dependencies

Run this inside all four projects:


npm install @nestjs/microservices @grpc/grpc-js @grpc/proto-loader					

Step 4: Create a Shared proto Files

Purpose :

Proto files define the contracts between microservices.

Each microservice now has its own proto file, stored in a shared proto directory.

Folder Structure:

Open proto/user.proto


syntax = "proto3";

package user;

service UserService {
  rpc GetUsers (Empty) returns (UserList);
}

message Empty {}

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

message UserList {
  repeated User users = 1;
}						

open proto/order.proto


syntax = "proto3";

package order;

service OrderService {
  rpc GetOrders (Empty) returns (OrderList);
}

message Empty {}

message Order {
  int32 id = 1;
  string item = 2;
}

message OrderList {
  repeated Order orders = 1;
}						

open proto/payment.proto


syntax = "proto3";

package payment;

service PaymentService {
  rpc GetPayments (Empty) returns (PaymentList);
}

message Empty {}

message Payment {
  int32 id = 1;
  int32 amount = 2;
  string status = 3;
}

message PaymentList {
  repeated Payment payments = 1;
}						

Note: Proto files are shared across services and stored in a central proto directory. Each microservice exposes and consumes only the proto files it requires.

Step 5: Configure User Service (gRPC Server)

open user-service/src/main.ts


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

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.GRPC,
      options: {
        url: 'localhost:5001',
        package: 'user',
        protoPath: join(process.cwd(), '../proto/user.proto'),
      },
    },
  );

  await app.listen();
  console.log('User gRPC Service running on port 5001');
}
bootstrap();						

Step 6: User gRPC Controller

open user-service/src/app.controller.ts


import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

@Controller()
export class AppController {

  @GrpcMethod('UserService', 'GetUsers')
  getUsers() {
    return {
      users: [
        { id: 1, name: 'Amit' },
        { id: 2, name: 'Rahul' },
      ],
    };
  }
}						

Step 7: Order Service (gRPC)

open order-service/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: {
        url: 'localhost:5002',
        package: 'order',
        protoPath: join(process.cwd(), '../proto/order.proto'),
      },
    },
  );

  await app.listen();
  console.log('Order gRPC Service running on port 5002');
}
bootstrap();					

Open order-service/src/app.controller.ts


import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

@Controller()
export class AppController {

  @GrpcMethod('OrderService', 'GetOrders')
  getOrders() {
    return {
      orders: [
        { id: 1, item: 'Laptop' },
        { id: 2, item: 'Phone' },
      ],
    };
  }
}					

Step 8: Payment Service (gRPC)

open payment-service/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: {
        url: 'localhost:5003',
        package: 'payment',
        protoPath: join(process.cwd(), '../proto/payment.proto'),
      },
    },
  );

  await app.listen();
  console.log('Payment gRPC Service running on port 5003');
}
bootstrap();					

open payment-service/src/app.controller.ts


import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

@Controller()
export class AppController {

  @GrpcMethod('PaymentService', 'GetPayments')
  getPayments() {
    return {
      payments: [
        { id: 1, amount: 5000, status: 'SUCCESS' },
        { id: 2, amount: 2000, status: 'PENDING' },
      ],
    };
  }
}					

Step 9: API Gateway (gRPC Client)

open api-gateway/src/app.module.ts


import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppController } from './app.controller';

@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'USER_SERVICE',
        transport: Transport.GRPC,
        options: {
          url: 'localhost:5001',
          package: 'user',
          protoPath: join(process.cwd(), '../proto/user.proto'),
        },
      },
      {
        name: 'ORDER_SERVICE',
        transport: Transport.GRPC,
        options: {
          url: 'localhost:5002',
          package: 'order',
          protoPath: join(process.cwd(), '../proto/order.proto'),
        },
      },
      {
        name: 'PAYMENT_SERVICE',
        transport: Transport.GRPC,
        options: {
          url: 'localhost:5003',
          package: 'payment',
          protoPath: join(process.cwd(), '../proto/payment.proto'),
        },
      },
    ]),
  ],
  controllers: [AppController],
})
export class AppModule {}					

Step 10: API Gateway Controller

open api-gateway/src/app.controller.ts


import { Controller, Get, Inject, OnModuleInit } from '@nestjs/common';
import type { ClientGrpc } from '@nestjs/microservices';

@Controller()
export class AppController implements OnModuleInit {
  private userService;
  private orderService;
  private paymentService;

  constructor(
    @Inject('USER_SERVICE') private userClient: ClientGrpc,
    @Inject('ORDER_SERVICE') private orderClient: ClientGrpc,
    @Inject('PAYMENT_SERVICE') private paymentClient: ClientGrpc,
  ) {}

  onModuleInit() {
    this.userService = this.userClient.getService('UserService');
    this.orderService = this.orderClient.getService('OrderService');
    this.paymentService = this.paymentClient.getService('PaymentService');
  }

  @Get('users')
  getUsers() {
    return this.userService.GetUsers({});
  }

  @Get('orders')
  getOrders() {
    return this.orderService.GetOrders({});
  }

  @Get('payments')
  getPayments() {
    return this.paymentService.GetPayments({});
  }
}					

Step 11: Run All Services

Start services in separate terminals:

1# cd user-service && npm run start

2# cd order-service && npm run start

3# cd payment-service && npm run start

4# cd api-gateway && npm run start

User → 5001

Order → 5002

Payment → 5003

API Gateway → 3000

Step 12: Test in Browser

http://localhost:3000/users

http://localhost:3000/orders

http://localhost:3000/payments

Now you will see user data coming from the microservice, like

#1 For User

#2 For Orders

#3 For Payments

Conclusion

In this project, we:

Built the same microservice system using gRPC

Compared Kafka vs gRPC practically

Understood real-world usage scenarios

Learned how NestJS supports multiple transports

Ready to Build Something Amazing?

Get in touch with Prishusoft – your trusted partner for custom software development. Whether you need a powerful web application or a sleek mobile app, our expert team is here to turn your ideas into reality.

image