npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-grpc

v1.5.2

Published

A lightweight NestJS package for type-safe gRPC communication between microservices

Readme

nestjs-grpc

npm version License: MIT TypeScript NestJS Test Coverage Node.js Version

Production-ready NestJS package for type-safe gRPC microservices with controller-based architecture

InstallationQuick StartAPI DocsExamplesConfiguration

✨ Features

  • 🎯 Controller-Based - Familiar NestJS patterns for gRPC handlers
  • 🛡️ Type-Safe - Full TypeScript support with auto-generated types from proto files
  • 🔄 All Streaming Patterns - Unary, server, client, and bidirectional streaming
  • Production-Ready - Connection pooling, caching, and optimized for high performance
  • 🛠️ CLI Generation - Generate TypeScript types from proto files with nestjs-grpc generate
  • 🔒 TLS & Security - Built-in TLS support and flexible authentication
  • 📊 Configurable Logging - GrpcLogLevel enum with DEBUG, VERBOSE, LOG, WARN, ERROR
  • 🔌 Full DI Support - Inject any NestJS service into controllers and clients
  • 🔍 Exception Classes - gRPC-specific exceptions with proper status codes
  • 🔁 Retry Logic - Built-in exponential backoff and automatic retries
  • 📡 Auto Connection Pooling - Automatic client management with cleanup

Installation

npm install nestjs-grpc

Quick Start

1. Create a Proto File

// protos/auth.proto
syntax = "proto3";
package auth;

service AuthService {
  rpc ValidateToken(ValidateTokenRequest) returns (ValidateTokenResponse);
  rpc Login(LoginRequest) returns (LoginResponse);
  rpc StreamUsers(StreamUsersRequest) returns (stream User);
}

message ValidateTokenRequest {
  string token = 1;
}

message ValidateTokenResponse {
  bool valid = 1;
  User user = 2;
}

message LoginRequest {
  string email = 1;
  string password = 2;
}

message LoginResponse {
  string token = 1;
  User user = 2;
}

message StreamUsersRequest {
  int32 limit = 1;
}

message User {
  string id = 1;
  string email = 2;
  string name = 3;
}

2. Generate TypeScript Types

npx nestjs-grpc generate --proto "./protos/**/*.proto" --output "./src/generated"

3. Setup Server Module

// app.module.ts
import { Module } from '@nestjs/common';
import { GrpcModule, GrpcLogLevel } from 'nestjs-grpc';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';

@Module({
  imports: [
    GrpcModule.forProvider({
      protoPath: './protos/auth.proto',
      package: 'auth',
      url: '0.0.0.0:50051',
      logging: {
        enabled: true,
        level: GrpcLogLevel.DEBUG,
        context: 'GrpcModule',
      },
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService],
})
export class AppModule {}

4. Create gRPC Controller

// auth.controller.ts
import { Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { GrpcController, GrpcMethod, GrpcStream, GrpcException } from 'nestjs-grpc';
import { ValidateTokenRequest, ValidateTokenResponse, LoginRequest, LoginResponse, StreamUsersRequest, User } from './generated/auth';

@GrpcController('AuthService')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @GrpcMethod('ValidateToken')
  async validateToken(request: ValidateTokenRequest): Promise<ValidateTokenResponse> {
    try {
      const isValid = await this.authService.validateToken(request.token);
      const user = isValid ? await this.authService.getUserFromToken(request.token) : null;
      return { valid: isValid, user };
    } catch (error) {
      throw GrpcException.internal('Token validation failed');
    }
  }

  @GrpcMethod('Login')
  async login(request: LoginRequest): Promise<LoginResponse> {
    const user = await this.authService.validateUser(request.email, request.password);
    if (!user) {
      throw GrpcException.unauthenticated('Invalid credentials');
    }
    const token = await this.authService.generateToken(user);
    return { token, user: { id: user.id, email: user.email, name: user.name } };
  }

  @GrpcStream('StreamUsers')
  streamUsers(request: StreamUsersRequest): Observable<User> {
    return new Observable(observer => {
      this.authService.findAllPaginated(request.limit)
        .then(users => {
          users.forEach(user => observer.next(user));
          observer.complete();
        })
        .catch(() => observer.error(GrpcException.internal('Failed to stream users')));
    });
  }
}

5. Use the Service (from another microservice)

import { Injectable } from '@nestjs/common';
import { GrpcService, GrpcClientService } from 'nestjs-grpc';

@GrpcService({
  serviceName: 'AuthService',
  package: 'auth',
  url: 'auth-service:50051',
  clientOptions: {
    timeout: 30000,
    maxRetries: 3,
    retryDelay: 1000,
  },
})
@Injectable()
export class AuthServiceClient {
  constructor(private readonly grpcClient: GrpcClientService) {}

  async validateToken(token: string): Promise<ValidateTokenResponse> {
    return this.grpcClient.call('AuthService', 'ValidateToken', { token });
  }
}

Decorators

@GrpcController

Marks a class as a gRPC service handler:

@GrpcController('ServiceName')
export class ServiceController {}

// With options
@GrpcController({ serviceName: 'ServiceName', package: 'custom.package' })
export class ServiceController {}

@GrpcMethod

Handles unary (single request/response) calls:

// Auto-infer method name from function name
@GrpcMethod()
async validateToken(request: ValidateTokenRequest): Promise<ValidateTokenResponse> {}

// Explicit method name
@GrpcMethod('ValidateToken')
async validateToken(request: ValidateTokenRequest): Promise<ValidateTokenResponse> {}

// With custom timeout
@GrpcMethod({ methodName: 'ProcessPayment', timeout: 60000 })
async processPayment(request: PaymentRequest): Promise<PaymentResponse> {}

@GrpcStream

Handles server streaming (multiple responses for one request):

@GrpcStream('StreamUsers')
streamUsers(request: StreamUsersRequest): Observable<User> {
  return new Observable(observer => {
    this.userService.findAllPaginated(request.limit)
      .then(users => {
        users.forEach(user => observer.next(user));
        observer.complete();
      })
      .catch(error => observer.error(GrpcException.internal('Failed to stream users')));
  });
}

@GrpcPayload

Extract request payload and metadata from gRPC context:

@GrpcMethod('CreateUser')
async createUser(
  @GrpcPayload() request: CreateUserRequest,
  @GrpcPayload('metadata') metadata?: any,
): Promise<CreateUserResponse> {
  console.log('Metadata:', metadata);
  return this.userService.create(request);
}

@GrpcService

Register a class as a gRPC service client:

@GrpcService({
  serviceName: 'AuthService',
  package: 'auth',
  url: 'auth-service:50051',
  clientOptions: {
    timeout: 30000,
    maxRetries: 3,
  },
})
@Injectable()
export class AuthServiceClient {
  constructor(private readonly grpcClient: GrpcClientService) {}
}

Client Service

GrpcClientService provides methods for all gRPC call types. Use these in your services to call other gRPC services:

Unary Calls (Single Request/Response)

@Injectable()
export class AuthService {
  constructor(private readonly grpcClient: GrpcClientService) {}

  async validateToken(token: string): Promise<ValidateTokenResponse> {
    return this.grpcClient.call<ValidateTokenRequest, ValidateTokenResponse>(
      'AuthService',
      'ValidateToken',
      { token },
      { timeout: 5000, maxRetries: 2, retryDelay: 1000 }
    );
  }
}

Server Streaming (Multiple Responses)

getUserStream(limit: number): Observable<User> {
  return this.grpcClient.serverStream<StreamUsersRequest, User>(
    'UserService',
    'StreamUsers',
    { limit }
  );
}

Client Streaming (Multiple Requests)

async uploadFile(fileChunks: Observable<FileChunk>): Promise<UploadResponse> {
  return this.grpcClient.clientStream<FileChunk, UploadResponse>(
    'FileService',
    'UploadFile',
    fileChunks
  );
}

Bidirectional Streaming (Multiple Requests/Responses)

startChat(messageStream: Observable<ChatMessage>): Observable<ChatMessage> {
  return this.grpcClient.bidiStream<ChatMessage, ChatMessage>(
    'ChatService',
    'StartChat',
    messageStream
  );
}

Error Handling

Use GrpcException to throw gRPC-compliant errors with proper status codes:

import { GrpcException } from 'nestjs-grpc';

@GrpcMethod('GetUser')
async getUser(request: GetUserRequest): Promise<GetUserResponse> {
  const user = await this.userService.findById(request.id);

  if (!user) {
    throw GrpcException.notFound('User not found', { userId: request.id });
  }

  if (!this.hasPermission(request.requesterId, user.id)) {
    throw GrpcException.permissionDenied('Access denied');
  }

  return { user };
}

Available Exception Types

All standard gRPC status codes:

GrpcException.ok()               // 0
GrpcException.cancelled()        // 1
GrpcException.unknown()          // 2
GrpcException.invalidArgument()  // 3
GrpcException.deadlineExceeded() // 4
GrpcException.notFound()         // 5
GrpcException.alreadyExists()    // 6
GrpcException.permissionDenied() // 7
GrpcException.resourceExhausted()// 8
GrpcException.failedPrecondition()// 9
GrpcException.aborted()          // 10
GrpcException.outOfRange()       // 11
GrpcException.unimplemented()    // 12
GrpcException.internal()         // 13
GrpcException.unavailable()      // 14
GrpcException.dataLoss()         // 15
GrpcException.unauthenticated()  // 16

Custom Exception Filter

Create a custom filter to handle errors globally:

import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { GrpcException } from 'nestjs-grpc';

@Catch()
export class CustomGrpcExceptionFilter implements RpcExceptionFilter<any> {
  catch(exception: any, host: ArgumentsHost): Observable<any> {
    if (exception instanceof GrpcException) {
      return throwError(() => ({
        code: exception.getCode(),
        message: exception.message,
        details: exception.getDetails(),
      }));
    }
    return throwError(() => ({
      code: 13, // INTERNAL
      message: 'Internal server error',
    }));
  }
}

// Register in module
@Module({
  imports: [GrpcModule.forProvider({ /* ... */ })],
  providers: [{ provide: APP_FILTER, useClass: CustomGrpcExceptionFilter }],
})
export class AppModule {}

Configuration Reference

Server Configuration

GrpcModule.forProvider({
  // Required
  protoPath: './protos/service.proto',  // Proto file path (supports glob patterns)
  package: 'service',                   // Proto package name

  // Connection
  url: '0.0.0.0:50051',                // Listen address (default: localhost:50051)
  secure: false,                        // Enable TLS

  // TLS Certificates
  rootCerts?: Buffer,
  privateKey?: Buffer,
  certChain?: Buffer,

  // Message Limits
  maxSendMessageSize: 4 * 1024 * 1024,  // Default: 4MB
  maxReceiveMessageSize: 4 * 1024 * 1024,

  // Logging
  logging: {
    enabled: true,
    level: GrpcLogLevel.LOG,
    context: 'GrpcModule',
  },
})

Client Configuration

@GrpcService({
  // Required
  serviceName: 'ExternalService',       // Service name from proto
  package: 'external',                  // Proto package name
  url: 'external-service:50051',        // Service URL

  // Connection
  secure: false,                        // Enable TLS

  // Timeouts & Retries
  clientOptions: {
    timeout: 30000,       // Request timeout in ms (default: 30000)
    maxRetries: 3,        // Retry attempts (default: 3)
    retryDelay: 1000,     // Delay between retries in ms (default: 1000)
  },

  // Advanced gRPC channel options
  channelOptions: {
    'grpc.keepalive_time_ms': 120000,
    'grpc.max_concurrent_streams': 100,
  },
})

Async Configuration

Use ConfigService for dynamic settings:

@Module({
  imports: [
    GrpcModule.forProviderAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        protoPath: config.get('GRPC_PROTO_PATH'),
        package: config.get('GRPC_PACKAGE'),
        url: config.get('GRPC_URL'),
        logging: {
          enabled: config.get('GRPC_LOGGING_ENABLED') !== 'false',
          level: process.env.NODE_ENV === 'production' ? GrpcLogLevel.WARN : GrpcLogLevel.DEBUG,
        },
      }),
    }),
  ],
})
export class AppModule {}

CLI Code Generation

Generate TypeScript types from proto files:

# Basic usage
npx nestjs-grpc generate --proto "./protos/**/*.proto" --output "./src/generated"

# Generate classes instead of interfaces
npx nestjs-grpc generate --classes

# Watch mode (regenerate on changes)
npx nestjs-grpc generate --watch

# Filter by package name
npx nestjs-grpc generate --package-filter "auth"

# Verbose output for debugging
npx nestjs-grpc generate --verbose

CLI Options

| Option | Description | Default | |--------|-------------|---------| | -p, --proto <pattern> | Proto file path or glob pattern | "./protos/**/*.proto" | | -o, --output <dir> | Output directory | "./src/generated" | | -w, --watch | Watch mode | false | | -c, --classes | Generate classes instead of interfaces | false | | -f, --package-filter <name> | Filter by package name | - | | -r, --recursive | Search directories recursively | true | | -v, --verbose | Verbose logging | false |


Testing

Testing Controllers

import { Test, TestingModule } from '@nestjs/testing';
import { GrpcModule } from 'nestjs-grpc';
import { AuthController } from './auth.controller';

describe('AuthController', () => {
  let controller: AuthController;
  let authService: AuthService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [
        GrpcModule.forProvider({
          protoPath: './test/protos/auth.proto',
          package: 'auth',
          url: 'localhost:50051',
        }),
      ],
      controllers: [AuthController],
      providers: [{
        provide: AuthService,
        useValue: {
          validateToken: jest.fn(),
          generateToken: jest.fn(),
        },
      }],
    }).compile();

    controller = module.get<AuthController>(AuthController);
    authService = module.get<AuthService>(AuthService);
  });

  it('should validate token successfully', async () => {
    jest.spyOn(authService, 'validateToken').mockResolvedValue(true);
    const result = await controller.validateToken({ token: 'valid-token' });
    expect(result.valid).toBe(true);
  });
});

Testing Streaming

import { Subject } from 'rxjs';

it('should handle server streaming', async () => {
  const mockStream = new Subject<User>();
  jest.spyOn(grpcClient, 'serverStream').mockReturnValue(mockStream);

  const result$ = service.getUserStream(10);
  const emitted: User[] = [];
  result$.subscribe(user => emitted.push(user));

  mockStream.next({ id: '1', name: 'John', email: '[email protected]' });
  mockStream.complete();

  expect(emitted).toHaveLength(1);
});

Logging

Configure logging using GrpcLogLevel enum:

logging: {
  enabled: true,
  level: GrpcLogLevel.DEBUG,  // DEBUG | VERBOSE | LOG | WARN | ERROR
  context: 'GrpcModule',
}

Environment-based logging:

logging: {
  enabled: config.get('GRPC_LOGGING_ENABLED') !== 'false',
  level: process.env.NODE_ENV === 'development'
    ? GrpcLogLevel.DEBUG
    : GrpcLogLevel.LOG,
}

Performance Tips

Connection Pooling

Clients are automatically pooled and reused with a 5-minute TTL:

// No configuration needed - automatic connection management

Message Size Optimization

Increase limits for larger payloads:

GrpcModule.forProvider({
  // ...
  maxSendMessageSize: 16 * 1024 * 1024,    // 16MB
  maxReceiveMessageSize: 16 * 1024 * 1024,
})

Retry Configuration

Enable retries with exponential backoff:

const response = await this.grpcClient.call(
  'ServiceName',
  'MethodName',
  request,
  {
    timeout: 5000,
    maxRetries: 3,
    retryDelay: 1000,  // Exponential: 1s → 2s → 4s
  }
);

Troubleshooting

Proto File Not Found

Ensure the path is correct and use absolute paths when possible:

import path from 'path';

GrpcModule.forProvider({
  protoPath: path.join(__dirname, '../protos/service.proto'),
  package: 'service',
})

Package Name Mismatch

The package name must match what's in your proto file:

// service.proto
package my.service;  // Must match configuration
GrpcModule.forProvider({
  protoPath: './protos/service.proto',
  package: 'my.service',  // Matches proto file
})

"12 UNIMPLEMENTED" Errors

Method is not registered. Check:

  1. Controller decorator service name matches proto
  2. Method decorator name matches proto exactly
  3. Controller is in module's controllers array
@GrpcController('ServiceName')  // Must match proto service
export class ServiceController {
  @GrpcMethod('MethodName')     // Must match proto method
  async methodName() {}
}

Connection Refused

Check server is running and URL is correct:

GrpcModule.forProvider({
  url: 'localhost:50051',  // Verify server is listening here
  logging: { level: GrpcLogLevel.DEBUG },  // Enable debug logs
})

Real-World Example

See BackendWorks/nestjs-microservices for a complete working example of a microservices architecture using nestjs-grpc.


Contributing

We welcome contributions! Please see our Contributing Guide.

Development Setup

git clone https://github.com/hmake98/nestjs-grpc.git
cd nestjs-grpc
npm install
npm run build
npm test

Quality Standards

  • Test Coverage: 99.87% statement coverage
  • TypeScript: Strict type checking
  • Linting: ESLint
  • Formatting: Prettier

Build Commands

npm run build        # Full production build (recommended)
npm run compile      # SWC compilation only (~31ms)
npm run declarations # Generate TypeScript declarations
npm test             # Run tests with coverage
npm run test:watch   # Watch mode

Release Commands

npm run release:patch    # Patch version (1.2.3 → 1.2.4)
npm run release:minor    # Minor version (1.2.3 → 1.3.0)
npm run release:major    # Major version (1.2.3 → 2.0.0)
npm run release:beta     # Beta release
npm run release:rc       # Release candidate

Changelog

v1.5.0 (Latest)

✨ Features

  • 99.87% statement coverage across core modules
  • Advanced error handling and streaming tests
  • SWC compiler integration (31ms vs 500ms+ with TypeScript)

🐛 Fixes

  • Fixed streaming error paths in all patterns
  • Resolved exception filter edge cases
  • Improved proto file loading error handling

📈 Performance

  • Lightning-fast SWC compilation
  • Optimized connection pooling
  • Enhanced retry logic with exponential backoff

🔧 Breaking Changes

  • Removed automatic global exception filter
  • Updated logging system to use GrpcLogLevel enum (removed deprecated options)
  • ts-jest replaced with @swc/jest for faster tests

For complete changelog, see GitHub Releases.


Links


License

MIT License - see LICENSE file for details.


Support


Made with ❤️ for the NestJS community

⭐ Star us on GitHub if this package helped you!