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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nam088/nestjs-grpc

v1.1.0

Published

A modern gRPC client library for NestJS with full streaming support and smart retry strategies

Readme

NestJS gRPC Client

npm version License: MIT TypeScript NestJS

A modern, production-ready gRPC client library for NestJS with full streaming support, smart retry strategies, and comprehensive TypeScript types.


Quick Start

npm install @nam088/nestjs-grpc
// app.module.ts
import { GrpcClientModule } from '@nam088/nestjs-grpc';

@Module({
  imports: [
    GrpcClientModule.forRoot({
      services: [{
        protoPath: './proto/user.proto',
        package: 'user',
        serviceName: 'UserService',
        url: 'localhost:50051'
      }],
      retry: {
        maxAttempts: 3,
        initialBackoff: 1000,
        retryableCodes: [grpc.status.UNAVAILABLE]
      },
      logLevel: 'DEBUG'
    })
  ]
})
export class AppModule {}

// user.service.ts
@Injectable()
export class UserService {
  constructor(private grpc: GrpcClientService) {}

  getUser(id: string) {
    return this.grpc.call({
      package: 'user',
      service: 'UserService',
      method: 'GetUser',
      request: { id }
    });
  }
}

Features

Core Capabilities

  • Full gRPC Support - Unary, server streaming, client streaming, bidirectional streaming
  • RxJS Integration - Native Observable support with powerful operators
  • TypeScript First - Complete type safety with generics and JSDoc
  • Multi-Service - Manage multiple gRPC services in one module

Production Ready

  • Smart Retry - Exponential/linear/fixed backoff with jitter
  • Interceptors - Built-in logging, custom middleware support
  • Exception Mapping - Automatic gRPC → HTTP status code conversion
  • Well Tested - 46 unit tests, 87%+ code coverage

Developer Experience

  • Object-Based API - Clean, readable method calls
  • Comprehensive Docs - Full JSDoc with examples
  • NestJS Native - Seamless integration with NestJS ecosystem

Usage Examples

Basic Call

this.grpc.call({
  package: 'user',
  service: 'UserService',
  method: 'GetUser',
  request: { id: '123' },
  timeout: 5000 // Optional timeout in ms
}).subscribe(user => console.log(user));

With RxJS Operators

this.grpc.call({ ... }).pipe(
  timeout(5000),
  map(user => user.email),
  catchError(err => of(null))
).subscribe();

Server Streaming

this.grpc.callServerStream({
  package: 'chat',
  service: 'ChatService',
  method: 'StreamMessages',
  request: { roomId: 'room1' }
}).subscribe(message => console.log(message));

Client Streaming

const { stream, response } = this.grpc.callClientStream({
  package: 'upload',
  service: 'FileService',
  method: 'UploadFile'
});

// Send data
stream.next({ chunk: data1 });
stream.next({ chunk: data2 });
stream.complete();

// Get response
response.subscribe(result => console.log(result));

Bidirectional Streaming

const { stream, response } = this.grpc.callBidiStream({
  package: 'chat',
  service: 'ChatService',
  method: 'Chat'
});

// Send and receive simultaneously
stream.next({ text: 'Hello' });
response.subscribe(msg => console.log(msg));

Smart Retry

import { RetryStrategy } from '@nam088/nestjs-grpc';

this.grpc.call({ ... }).pipe(
  RetryStrategy.retry({
    maxAttempts: 3,
    backoffStrategy: 'exponential',
    retryableCodes: [grpc.status.UNAVAILABLE],
    initialBackoff: 1000,
    maxBackoff: 5000,
    backoffMultiplier: 2,
    enableJitter: true
  })
).subscribe();

With Interceptors

// app.module.ts
GrpcClientModule.forRoot({
  services: [...],
  interceptors: [LoggingInterceptor, MetricsInterceptor]
})

// Custom interceptor
@Injectable()
export class MetricsInterceptor implements GrpcInterceptor {
  intercept(options, next) {
    const start = Date.now();
    return next().pipe(
      tap(() => this.recordMetric(Date.now() - start))
    );
  }
}

Exception Handling

@Catch(GrpcException)
export class GrpcExceptionFilter implements ExceptionFilter {
  catch(exception: GrpcException, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();
    response.status(exception.getStatus()).json({
      error: exception.details,
      code: exception.code
    });
  }
}

API Reference

GrpcClientModule

GrpcClientModule.forRoot({
  services: GrpcServiceConfig[],
  interceptors?: GrpcInterceptor[],
  isGlobal?: boolean,
  retry?: RetryConfig,
  logLevel?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
})

GrpcClientService

| Method | Description | Returns | |--------|-------------|---------| | call() | Unary call | Observable<TResponse> | | callServerStream() | Server streaming | Observable<TResponse> | | callClientStream() | Client streaming | { stream, response } | | callBidiStream() | Bidirectional | { stream, response } |

RetryStrategy

RetryStrategy.retry({
  maxAttempts?: number,           // Default: 3
  initialBackoff?: number,        // Default: 1000
  maxBackoff?: number,            // Default: 5000
  backoffStrategy?: 'exponential' | 'linear' | 'fixed',
  backoffMultiplier?: number,     // Default: 2
  retryableCodes?: grpc.status[],
  enableJitter?: boolean          // Default: true
})

Configuration

Basic Setup

GrpcClientModule.forRoot({
  services: [{
    protoPath: './proto/user.proto',
    package: 'user',
    serviceName: 'UserService',
    url: 'localhost:50051'
  }]
})

Multiple Services

GrpcClientModule.forRoot({
  services: [
    {
      protoPath: './proto/user.proto',
      package: 'user',
      serviceName: 'UserService',
      url: 'localhost:50051'
    },
    {
      protoPath: './proto/order.proto',
      package: 'order',
      serviceName: 'OrderService',
      url: 'localhost:50052'
    }
  ],
  isGlobal: true
})

With Credentials

import * as grpc from '@grpc/grpc-js';

GrpcClientModule.forRoot({
  services: [{
    protoPath: './proto/user.proto',
    package: 'user',
    serviceName: 'UserService',
    url: 'api.example.com:443',
    credentials: grpc.credentials.createSsl()
  }]
})

Testing

describe('UserService', () => {
  let service: UserService;
  let grpcClient: GrpcClientService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      imports: [GrpcClientModule.forRoot({ services: [...] })],
      providers: [UserService]
    }).compile();

    service = module.get<UserService>(UserService);
    grpcClient = module.get<GrpcClientService>(GrpcClientService);
  });

  it('should get user', async () => {
    const result = await lastValueFrom(service.getUser('123'));
    expect(result).toBeDefined();
  });
});

Migration Guide

From @grpc/grpc-js

// Before
const client = new UserServiceClient('localhost:50051');
client.getUser({ id: '123' }, (err, response) => {
  if (err) throw err;
  console.log(response);
});

// After
this.grpc.call({
  package: 'user',
  service: 'UserService',
  method: 'GetUser',
  request: { id: '123' }
}).subscribe({
  next: response => console.log(response),
  error: err => console.error(err)
});

Best Practices

  1. Use Retry for Production

    this.grpc.call({ ... }).pipe(
      RetryStrategy.retry({ maxAttempts: 3 })
    )
  2. Add Timeouts

    this.grpc.call({ ... }).pipe(timeout(5000))
  3. Handle Errors Gracefully

    this.grpc.call({ ... }).pipe(
      catchError(err => {
        this.logger.error(err);
        return of(defaultValue);
      })
    )
  4. Use Interceptors for Cross-Cutting Concerns

    // Logging, metrics, tracing
    GrpcClientModule.forRoot({
      interceptors: [LoggingInterceptor, MetricsInterceptor]
    })

License

MIT © 2025


Links