npm package: @merl1231/grpc-react-native
Description: gRPC client for React Native with new architecture support
Author: Merl111
Latest version: 0.1.1

# grpc-react-native

A gRPC client implementation for React Native with new architecture support.

## Features

- Unary gRPC calls
- Server streaming
- Client streaming
- Bidirectional streaming
- TypeScript support
- Android (Kotlin + gRPC-Java)
- iOS (Objective-C + gRPC-Objective-C)
- React Native new architecture support
- Deadline/timeout handling
- Metadata support
- Binary protobuf serialization

## Installation

```bash
npm install grpc-react-native
```

### iOS Setup

Add to your `ios/Podfile`:

```ruby
pod 'grpc-react-native', :path => '../node_modules/grpc-react-native'
```

Then run:

```bash
cd ios && pod install
```

### Android Setup

No additional setup required for Android.

## Usage

### Basic Usage

```typescript
import { GrpcClient } from 'grpc-react-native';

const client = new GrpcClient({
  host: 'api.example.com',
  port: 443,
  useTls: true
});

// Initialize the client
await client.initialize();

// Make a unary call
const response = await client.unaryCall(
  'com.example.Service/Method',
  { message: 'Hello World' }
);

console.log(response);

// Close the client
await client.close();
```

### Streaming Calls

#### Server Streaming

```typescript
// Server streaming returns an async generator
for await (const response of client.serverStreamCall(
  'com.example.Service/GetData',
  { query: 'search term' },
  { timeout: 30000 }
)) {
  if (response.done) {
    console.log('Stream completed');
    break;
  }
  console.log('Received:', response.data);
}
```

#### Client Streaming

```typescript
// Client streaming accepts a single request or async iterable
async function* generateRequests() {
  for (let i = 0; i < 10; i++) {
    yield new RequestImpl({ id: i, data: `chunk-${i}` });
  }
}

const response = await client.clientStreamCall(
  'com.example.Service/UploadData',
  generateRequests(),
  { timeout: 60000 }
);

console.log('Upload complete:', response.data);
```

#### Bidirectional Streaming

```typescript
// Bidirectional streaming for real-time communication
async function* sendMessages() {
  yield new ChatMessageImpl({ text: 'Hello' });
  yield new ChatMessageImpl({ text: 'World' });
}

for await (const response of client.bidirectionalStreamCall(
  'com.example.Service/Chat',
  sendMessages(),
  { timeout: 30000 }
)) {
  if (response.done) {
    break;
  }
  console.log('Received message:', response.data);
}
```

### With Generated Services

When using the `grpc-react-native-plugin`, you'll get generated service clients:

```typescript
import { AccountingServiceClient } from './generated/accounting';
import { BalanceRequestImpl } from './generated/accounting_types';

const client = new GrpcClient({
  host: 'api.neofs.com',
  port: 443,
  useTls: true
});

await client.initialize();

const accountingClient = new AccountingServiceClient(client);

// Unary call
const request = new BalanceRequestImpl({
  ownerId: 'some-owner-id'
});
const balance = await accountingClient.balance(request);

// Server streaming
for await (const response of accountingClient.listBalances(request)) {
  if (response.done) break;
  console.log('Balance:', response.data);
}
```

## API Reference

### GrpcClientOptions

```typescript
interface GrpcClientOptions {
  host: string;              // Server hostname
  port?: number;            // Server port (default: 443)
  useTls?: boolean;         // Use TLS (default: true)
  timeout?: number;         // Default request timeout in ms
  defaultDeadline?: number; // Default deadline in ms
}
```

### GrpcCallOptions

```typescript
interface GrpcCallOptions {
  timeout?: number;                    // Call timeout in ms (relative)
  deadline?: number;                   // Absolute deadline timestamp in ms
  metadata?: Record<string, string>;   // gRPC metadata
}
```

### GrpcClient Methods

#### `initialize(): Promise<void>`

Initialize the client and create a gRPC channel. Called automatically on first use.

#### `unaryCall<TRequest, TResponse>(method: string, request: TRequest, options?: GrpcCallOptions): Promise<GrpcResponse<TResponse>>`

Make a unary gRPC call.

- `method`: Full method name (e.g., `'com.example.Service/Method'`)
- `request`: Request object with `serializeBinary()` method or `Uint8Array`
- `options`: Optional call options
- Returns: Promise resolving to `GrpcResponse<TResponse>`

#### `serverStreamCall<TRequest, TResponse>(method: string, request: TRequest, options?: GrpcCallOptions): AsyncGenerator<GrpcStreamResponse<TResponse>>`

Make a server streaming gRPC call.

- Returns: Async generator yielding stream responses
- Each response has `data`, `metadata`, `done`, and optional `status`

#### `clientStreamCall<TRequest, TResponse>(method: string, request: TRequest | AsyncIterable<Uint8Array>, options?: GrpcCallOptions): Promise<GrpcResponse<TResponse>>`

Make a client streaming gRPC call.

- `request`: Single request object or async iterable of `Uint8Array`
- Returns: Promise resolving to final response

#### `bidirectionalStreamCall<TRequest, TResponse>(method: string, request: TRequest, options?: GrpcCallOptions): AsyncGenerator<GrpcStreamResponse<TResponse>>`

Make a bidirectional streaming gRPC call.

- Returns: Async generator yielding stream responses

#### `close(): Promise<void>`

Close the client connection and cleanup resources.

### Response Types

#### GrpcResponse

```typescript
interface GrpcResponse<T> {
  data: T;                           // Response data (Uint8Array)
  metadata?: Record<string, string>;  // Response metadata
  status: GrpcStatus;                // gRPC status
}
```

#### GrpcStreamResponse

```typescript
interface GrpcStreamResponse<T> {
  data?: T;                          // Stream data (Uint8Array)
  metadata?: Record<string, string>; // Response metadata
  status?: GrpcStatus;               // gRPC status (usually on final message)
  done: boolean;                     // Whether stream is complete
}
```

#### GrpcStatus

```typescript
interface GrpcStatus {
  code: GrpcStatusCode;  // gRPC status code
  message?: string;       // Error message
  details?: string;       // Error details
}
```

### Error Handling

The client throws `GrpcTimeoutError` when deadlines are exceeded:

```typescript
interface GrpcTimeoutError extends Error {
  code: 'DEADLINE_EXCEEDED';
  deadline: number;      // Absolute deadline timestamp
  elapsed: number;       // Time elapsed past deadline
}
```

Example error handling:

```typescript
try {
  const response = await client.unaryCall('Service/Method', request, {
    timeout: 5000
  });
} catch (error) {
  if (error.code === 'DEADLINE_EXCEEDED') {
    console.error('Request timed out');
  } else {
    console.error('gRPC error:', error);
  }
}
```

## Error Handling

The client provides comprehensive error handling:

- **Deadline Exceeded**: Thrown when a call exceeds its timeout/deadline
- **gRPC Errors**: Standard gRPC status codes (UNKNOWN, INVALID_ARGUMENT, etc.)
- **Connection Errors**: Network and connection-related errors

All errors include detailed information about the failure.

## Examples

### Complete Example

```typescript
import { GrpcClient } from 'grpc-react-native';
import { MyServiceClient } from './generated/my_service';
import { MyRequestImpl } from './generated/my_types';

async function example() {
  // Create client
  const client = new GrpcClient({
    host: 'api.example.com',
    port: 443,
    useTls: true,
    defaultDeadline: 30000 // 30 seconds
  });

  // Initialize (optional, done automatically)
  await client.initialize();

  // Use generated service client
  const serviceClient = new MyServiceClient(client);

  try {
    // Make a unary call
    const request = new MyRequestImpl({ id: '123' });
    const response = await serviceClient.getData(request, {
      timeout: 10000,
      metadata: { 'x-custom-header': 'value' }
    });
    console.log('Response:', response);

    // Make a streaming call
    for await (const chunk of serviceClient.streamData(request)) {
      if (chunk.done) break;
      console.log('Chunk:', chunk.data);
    }
  } catch (error) {
    if (error.code === 'DEADLINE_EXCEEDED') {
      console.error('Request timed out');
    } else {
      console.error('Error:', error);
    }
  } finally {
    // Cleanup
    await client.close();
  }
}
```

## Development

### Building

```bash
npm run build
```

### Building native code (Android / iOS)

To verify that the Android and iOS implementations compile:

- **Android**  
  Requires **Gradle 8.9** (the project’s plugins are not compatible with Gradle 9). Options:
  - Use Docker:  
    `npm run build:android` (uses `gradle:8.9-jdk17` if Docker is available)
  - Or install Gradle 8.9 (e.g. `sdk install gradle 8.9`) and run:  
    `gradle :android:assembleRelease`  
  - Or generate the wrapper with Gradle 8.9 and then use:  
    `./gradlew :android:assembleRelease`

- **iOS**  
  Requires **CocoaPods** and **Xcode**. From the project root:
  - `npm run build:ios`  
  or `cd ios && pod lib lint grpc-react-native.podspec --allow-warnings`

- **Both**  
  `npm run build:native` (runs Android then iOS; Android will use Docker if available and no local Gradle 8.9 wrapper is present).

### Testing

```bash
npm test
```

### Project Structure

```
grpc-react-native/
├── src/
│   └── index.ts          # Main TypeScript client
├── android/              # Android native module (Kotlin)
├── ios/                  # iOS native module (Swift)
└── lib/                  # Compiled JavaScript output
```

## Requirements

- React Native 0.60+
- Android: gRPC-Java (included)
- iOS: gRPC-Swift (via CocoaPods)

## License

MIT