npm package: @nam088/nestjs-grpc
Description: A modern gRPC client library for NestJS with full streaming support and smart retry strategies
Latest version: 1.1.0

# NestJS gRPC Client

[![npm version](https://img.shields.io/npm/v/@nam088/nestjs-grpc.svg)](https://www.npmjs.com/package/@nam088/nestjs-grpc)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/)
[![NestJS](https://img.shields.io/badge/NestJS-10%20%7C%2011-red)](https://nestjs.com/)

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

---

## Quick Start

```bash
npm install @nam088/nestjs-grpc
```

```typescript
// 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

```typescript
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

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

### Server Streaming

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

### Client Streaming

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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

```typescript
@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

```typescript
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

```typescript
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

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

### Multiple Services

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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**
   ```typescript
   this.grpc.call({ ... }).pipe(
     RetryStrategy.retry({ maxAttempts: 3 })
   )
   ```

2. **Add Timeouts**
   ```typescript
   this.grpc.call({ ... }).pipe(timeout(5000))
   ```

3. **Handle Errors Gracefully**
   ```typescript
   this.grpc.call({ ... }).pipe(
     catchError(err => {
       this.logger.error(err);
       return of(defaultValue);
     })
   )
   ```

4. **Use Interceptors for Cross-Cutting Concerns**
   ```typescript
   // Logging, metrics, tracing
   GrpcClientModule.forRoot({
     interceptors: [LoggingInterceptor, MetricsInterceptor]
   })
   ```

---

## License

MIT © 2025

---

## Links

- [GitHub Repository](https://github.com/nam088/nestjs-grpc)
- [npm Package](https://www.npmjs.com/package/@nam088/nestjs-grpc)
- [Issue Tracker](https://github.com/nam088/nestjs-grpc/issues)
- [gRPC Documentation](https://grpc.io/docs/)
- [NestJS Documentation](https://docs.nestjs.com/)