Skip to main content

@globalart/nestjs-grpc

A NestJS module for working with gRPC clients and servers. Provides a convenient API for creating and using gRPC clients, as well as configuring gRPC servers with health checks and reflection support.

Installation​

npm install @globalart/nestjs-grpc @nestjs/microservices

Overview​

The @globalart/nestjs-grpc package simplifies working with gRPC in NestJS applications by providing:

  • Easy client creation - Configure multiple gRPC clients through configuration
  • Type safety - Full TypeScript support with proper types
  • Abstract client - Simplified API for calling gRPC methods
  • Server configuration - Ready-to-use configuration with health checks and reflection
  • Global module - Available throughout your application

Key Features​

  • Multiple clients - Configure multiple gRPC clients with unique names
  • Abstract client - Base class for simplifying gRPC calls
  • Health Checks - Automatic gRPC health check setup for servers
  • Reflection - gRPC reflection support for development
  • Type safety - Full TypeScript support

Quick Start​

Module Setup​

Import GrpcModule in your root module:

import { Module } from "@nestjs/common";
import { GrpcModule } from "@globalart/nestjs-grpc";

@Module({
imports: [
GrpcModule.forRoot({
clients: [
{
clientName: "userService",
packageName: "user",
protoPath: "./proto/user.proto",
url: "localhost:5000",
},
{
clientName: "orderService",
packageName: "order",
protoPath: "./proto/order.proto",
url: "localhost:5001",
},
],
}),
],
})
export class AppModule {}

Using the Client​

Use the InjectGrpcClient decorator to inject the client:

import { Injectable } from "@nestjs/common";
import { InjectGrpcClient, AbstractGrpcClient } from "@globalart/nestjs-grpc";
import { ClientGrpc } from "@nestjs/microservices";
import { Observable } from "rxjs";

interface UserService {
getUser(data: { id: string }): Observable<{ id: string; name: string }>;
createUser(data: { name: string }): Observable<{ id: string; name: string }>;
}

@Injectable()
export class UserService extends AbstractGrpcClient {
constructor(
@InjectGrpcClient("userService")
private readonly client: ClientGrpc
) {
super(client);
}

async getUserById(id: string) {
return this.service<UserService>("UserService").call("getUser", { id });
}

async createUser(name: string) {
return this.service<UserService>("UserService").call("createUser", {
name,
});
}
}

Configuring gRPC Server​

Use getGrpcConfig to configure a gRPC server:

import { NestFactory } from "@nestjs/core";
import { MicroserviceOptions } from "@nestjs/microservices";
import { getGrpcConfig } from "@globalart/nestjs-grpc";
import { AppModule } from "./app.module";

async function bootstrap() {
const app = await NestFactory.create(AppModule);

const grpcOptions: MicroserviceOptions = getGrpcConfig({
url: "0.0.0.0:5000",
packageName: "user",
protoPath: "./proto/user.proto",
});

app.connectMicroservice(grpcOptions);
await app.startAllMicroservices();
await app.listen(3000);
}

bootstrap();

Configuration​

GrpcModuleOptions​

OptionTypeRequiredDescription
clientsGrpcOptionsClient[]YesArray of gRPC client configurations

GrpcOptionsClient​

OptionTypeRequiredDefaultDescription
clientNamestringYes—Unique client name
packageNamestringYes—Proto package name
protoPathstringYes—Path to proto file
urlstringYes—gRPC server URL
deadlineMsnumberNononeDeadline of every attempt made through AbstractGrpcClient, in ms
maxRetriesnumberNoInfinityRetries after UNAVAILABLE or DEADLINE_EXCEEDED before the error is thrown
retryDelayMsnumberNo5000Delay between those retries, in ms

Deadlines and retries​

Without deadlineMs a call waits for the server as long as the connection stays open, and UNAVAILABLE / DEADLINE_EXCEEDED are retried forever. A server that stops answering a single request therefore blocks the caller indefinitely. Set a deadline and a retry budget on clients that run inside consumers or other loops that must keep moving:

GrpcModule.forRoot({
clients: [
{
clientName: "userService",
packageName: "user",
protoPath: join(__dirname, "proto/user.proto"),
url: "localhost:5000",
deadlineMs: 10000,
maxRetries: 2,
retryDelayMs: 1000,
},
],
});

The deadline is computed for every attempt, so a retry gets the full deadlineMs again. Once maxRetries is spent, the last UNAVAILABLE or DEADLINE_EXCEEDED error is thrown even when retryNullOnError is true: a transport failure is not reported as a missing result. Other errors keep the retryNullOnError behaviour.

Usage​

Create a client​

The abstract class simplifies working with gRPC calls:

import { Injectable } from "@nestjs/common";
import { InjectGrpcClient, AbstractGrpcClient } from "@globalart/nestjs-grpc";
import { ClientGrpc } from "@nestjs/microservices";
import { Observable } from "rxjs";

interface OrderService {
getOrder(data: { id: string }): Observable<{ id: string; total: number }>;
createOrder(data: { items: string[] }): Observable<{ id: string }>;
}

@Injectable()
export class OrderService extends AbstractGrpcClient {
constructor(
@InjectGrpcClient("orderService")
private readonly client: ClientGrpc
) {
super(client);
}

async getOrderById(id: string) {
return this.service<OrderService>("OrderService").call("getOrder", {
id,
});
}

async createOrder(items: string[]) {
return this.service<OrderService>("OrderService").call("createOrder", {
items,
});
}
}

Usage with metadata​

Sending metadata​

import { Injectable } from "@nestjs/common";
import { InjectGrpcService, GrpcService } from "@globalart/nestjs-grpc";

@Injectable()
export class MyService {
constructor(
@InjectGrpcService()
private readonly grpcService: GrpcService,
private readonly orderService: OrderService
) {}

async getOrder(id: string) {
this.grpcService.addMetadata("x-custom-header", "some-value");
return this.orderService.getOrderById(id);
}
}

Receiving metadata​

import { Injectable } from "@nestjs/common";
import { InjectGrpcService, GrpcService } from "@globalart/nestjs-grpc";

@Injectable()
export class MyService {
constructor(
@InjectGrpcService()
private readonly grpcService: GrpcService
) {}

async getOrder(id: string) {
const metadata = this.grpcService.getMetadata();
return metadata.get("x-custom-header");
}
}
warning

Warning: all clients must be used through an abstract client class in order to pass metadata via the microservices context

API Reference​

GrpcModule​

forRoot(options: GrpcOptions): DynamicModule​

Creates a global module with configured gRPC clients.

Parameters:

  • options - Module configuration with an array of clients

Returns: DynamicModule - Configured module

AbstractGrpcClient​

Base class for simplifying work with gRPC clients. Requires only a ClientGrpc instance in the constructor.

Calls a gRPC method and returns the result.

Parameters:

  • serviceName - Service name in the proto file
  • methodName - Method name to call
  • payload - Data to pass to the method

Type Parameters:

  • T - Interface describing the gRPC service methods
  • K - Key of the service interface (method name)

Returns: Promise with the call result

Example:

interface UserService {
getUser(data: { id: string }): Observable<{ id: string; name: string }>;
}

const result = await this.service<UserService>("UserService").call("getUser", {
id: "123",
});

InjectGrpcClient​

Decorator for injecting a gRPC client.

Parameters:

  • name - Client name specified in the configuration

Example:

constructor(
@InjectGrpcClient("userService")
private readonly client: ClientGrpc
) {}

getGrpcConfig​

Function for creating gRPC server configuration with health checks and reflection support.

Parameters:

  • options - Server configuration:
    • url - URL to listen on
    • packageName - Proto package name
    • protoPath - Path to proto file

Returns: MicroserviceOptions - Configuration for NestJS microservice

Example:

const grpcOptions = getGrpcConfig({
url: "0.0.0.0:5000",
packageName: "user",
protoPath: "./proto/user.proto",
});

Features​

Health Checks​

When using getGrpcConfig, the gRPC health check service is automatically configured. This allows you to use standard tools for service health checks.

Reflection​

gRPC reflection is also automatically configured, which simplifies development and debugging with tools like grpcurl or Postman.

Global Module​

GrpcModule is registered as a global module, meaning all configured clients are available in all application modules without needing to import GrpcModule in each module.

Usage Examples​

Error Handling​

import { Injectable } from "@nestjs/common";
import { InjectGrpcClient, AbstractGrpcClient } from "@globalart/nestjs-grpc";
import { ClientGrpc, RpcException } from "@nestjs/microservices";
import { Observable } from "rxjs";

interface UserService {
getUser(data: { id: string }): Observable<{ id: string; name: string }>;
}

@Injectable()
export class UserService extends AbstractGrpcClient {
constructor(
@InjectGrpcClient("userService")
private readonly client: ClientGrpc
) {
super(client);
}

async getUserById(id: string) {
try {
return await this.service<UserService>("UserService").call("getUser", {
id,
});
} catch (error) {
if (error instanceof RpcException) {
throw new Error(`gRPC error: ${error.message}`);
}
throw error;
}
}
}

Using Multiple Clients​

import { Module } from "@nestjs/common";
import { GrpcModule } from "@globalart/nestjs-grpc";

@Module({
imports: [
GrpcModule.forRoot({
clients: [
{
clientName: "userService",
packageName: "user",
protoPath: "./proto/user.proto",
url: "localhost:5000",
},
{
clientName: "orderService",
packageName: "order",
protoPath: "./proto/order.proto",
url: "localhost:5001",
},
{
clientName: "paymentService",
packageName: "payment",
protoPath: "./proto/payment.proto",
url: "localhost:5002",
},
],
}),
],
})
export class AppModule {}

Best Practices​

  • Use types - Define interfaces for your gRPC services for type safety
  • Handle errors - Always handle errors from gRPC calls
  • Use AbstractGrpcClient - Simplifies code and makes it more readable. Just pass ClientGrpc to the constructor
  • Specify types in call method - Always specify the service interface and method name as type parameters when calling call
  • Configure health checks - Use getGrpcConfig for automatic health check setup
  • Organize proto files - Store proto files in a separate directory

Troubleshooting​

Client Not Found​

If you get a "Grpc client not found" error, make sure:

  1. The client name in InjectGrpcClient matches the clientName in the configuration
  2. GrpcModule is imported in the root module
  3. The client is used after module initialization

Connection Errors​

If you experience connection issues:

  1. Check that the gRPC server is running and accessible
  2. Ensure the URL is specified correctly
  3. Check network settings and firewalls
  4. Make sure the proto file exists and the path is correct

Type Issues​

If TypeScript cannot determine types:

  1. Make sure you've defined an interface for your gRPC service
  2. Always specify both type parameters when calling the call method: call<ServiceInterface, "methodName">
  3. Verify that types in the interface match the proto definition
  4. Ensure the service interface methods return Observable<T> where T is the response type