@vync/grpcx
v0.1.0
Published
A React Native/Expo module for gRPC with full streaming support (unary, client, server, bidirectional)
Maintainers
Readme
grpcx
A React Native/Expo module for gRPC with full streaming support (unary, client streaming, server streaming, and bidirectional streaming).
Features
- All Streaming Types: Unary, Server Streaming, Client Streaming, and Bidirectional Streaming
- Runtime Proto Loading: No build-time code generation required
- Dynamic Service Discovery: Automatically discovers services and methods from proto files
- TypeScript Support: Full type definitions for all APIs
- Native Performance: Uses native gRPC libraries (gRPC-Java, gRPC-Swift)
- Connection Management: Automatic channel pooling and reconnection
- Metadata Support: Custom headers and trailers
- Error Handling: Proper gRPC status codes mapped to JavaScript errors
Installation
npm install grpcx
# or
yarn add grpcxiOS Setup
cd ios && pod installAndroid Setup
No additional steps required - Gradle dependencies are automatically handled.
Prerequisites
Proto Descriptor Files
This library requires proto descriptor files (.desc) alongside your .proto files. Generate them using protoc:
protoc --descriptor_set_out=service.desc --include_imports service.protoInclude both .proto and .desc files in your app bundle.
Quick Start
import { GrpcClient } from 'grpcx';
// Create a client
const client = new GrpcClient({
host: 'api.example.com',
port: 50051,
protoPath: '/path/to/service.proto',
useTls: true,
});
// Load the proto file
await client.load();
// Make a unary call
const response = await client.call(
'mypackage.MyService/MyMethod',
{ field1: 'value1', field2: 42 }
);
console.log(response);
// Clean up
await client.close();Usage Examples
Unary Calls
Single request, single response:
const response = await client.call(
'mypackage.MyService/MyMethod',
{ field1: 'value1', field2: 42 },
{
metadata: { 'authorization': 'Bearer token' },
timeout: 5000,
}
);
console.log(response);Server Streaming
Single request, stream of responses:
const stream = client.serverStream(
'mypackage.MyService/StreamMethod',
{ query: 'search term' }
);
stream.on('data', (data) => {
console.log('Received:', data);
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
stream.on('end', () => {
console.log('Stream completed');
});
// Cancel the stream if needed
// await stream.cancel();Client Streaming
Stream of requests, single response:
const stream = client.clientStream('mypackage.MyService/UploadMethod');
// Listen for the final response
stream.on('data', (response) => {
console.log('Final response:', response);
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
stream.on('end', () => {
console.log('Stream completed');
});
// Send multiple messages
await stream.write({ chunk: 1, data: '...' });
await stream.write({ chunk: 2, data: '...' });
await stream.write({ chunk: 3, data: '...' });
// Close the stream (triggers server to send final response)
await stream.end();Bidirectional Streaming
Stream of requests and responses:
const stream = client.bidiStream('mypackage.MyService/ChatMethod');
// Listen for responses
stream.on('data', (message) => {
console.log('Received:', message);
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
stream.on('end', () => {
console.log('Stream ended');
});
// Send messages
await stream.write({ message: 'Hello' });
await stream.write({ message: 'World' });
// Close when done
await stream.end();Service Discovery
// Get all available services
const services = client.getServices();
console.log(services);
// Get a specific service
const service = client.getService('mypackage.MyService');
console.log(service.methods);
// Get a specific method
const method = client.getMethod('mypackage.MyService/MyMethod');
console.log(method.type); // UNARY, SERVER_STREAMING, etc.API Reference
GrpcClient
Constructor
new GrpcClient(config: GrpcClientConfig)Config Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| host | string | required | Server hostname |
| port | number | 443 | Server port |
| protoPath | string | required | Path to .proto file in app bundle |
| useTls | boolean | true | Use TLS/SSL |
| timeout | number | 30000 | Default timeout in milliseconds |
| maxRetries | number | 3 | Max retry attempts |
| metadata | object | {} | Default metadata for all calls |
Methods
load(): Promise<void>
Load and parse the proto file. Must be called before making any RPC calls.
call<TRequest, TResponse>(method: string, request: TRequest, options?: CallOptions): Promise<TResponse>
Make a unary call.
Parameters:
method- Full method name (e.g., 'package.Service/Method')request- Request objectoptions- Optional call options (metadata, timeout)
Returns: Promise resolving to response object
serverStream<TRequest>(method: string, request: TRequest, options?: CallOptions): ServerStream
Create a server streaming call.
Parameters:
method- Full method namerequest- Request objectoptions- Optional call options
Returns: ServerStream instance
clientStream<TResponse>(method: string, options?: CallOptions): ClientStream
Create a client streaming call.
Parameters:
method- Full method nameoptions- Optional call options
Returns: ClientStream instance
bidiStream(method: string, options?: CallOptions): BidiStream
Create a bidirectional streaming call.
Parameters:
method- Full method nameoptions- Optional call options
Returns: BidiStream instance
getServices(): GrpcServiceDescriptor[]
Get all available services from the loaded proto.
Returns: Array of service descriptors
getService(serviceName: string): GrpcServiceDescriptor | undefined
Get a specific service descriptor.
Parameters:
serviceName- Full service name
Returns: Service descriptor or undefined
getMethod(fullMethodName: string): GrpcMethodDescriptor | undefined
Get a specific method descriptor.
Parameters:
fullMethodName- Full method name (e.g., 'package.Service/Method')
Returns: Method descriptor or undefined
close(): Promise<void>
Close the client and cleanup all resources.
GrpcStream
Base class for all stream types. Extends EventEmitter.
Events
data: Emitted when data is receivederror: Emitted on errorend: Emitted when stream completesmetadata: Emitted when metadata is receivedstatus: Emitted with gRPC status
Methods
write(data: any): Promise<void>
Write data to the stream (client/bidi streams only).
end(): Promise<void>
End the stream (half-close).
cancel(): Promise<void>
Cancel the stream.
getId(): string
Get the stream ID.
TypeScript Support
Full TypeScript definitions are included. All types are exported from the main package:
import {
GrpcClient,
GrpcClientConfig,
GrpcMethodType,
GrpcStatusCode,
GrpcError,
ServerStream,
ClientStream,
BidiStream,
} from 'grpcx';Error Handling
Errors follow gRPC status codes:
import { GrpcStatusCode } from 'grpcx';
try {
const response = await client.call('MyService/MyMethod', request);
} catch (error) {
if (error.code === GrpcStatusCode.UNAUTHENTICATED) {
// Handle auth error
} else if (error.code === GrpcStatusCode.NOT_FOUND) {
// Handle not found
}
console.error(error.message);
}gRPC Status Codes
| Code | Name | Description | |------|------|-------------| | 0 | OK | Success | | 1 | CANCELLED | Operation cancelled | | 2 | UNKNOWN | Unknown error | | 3 | INVALID_ARGUMENT | Invalid argument | | 4 | DEADLINE_EXCEEDED | Deadline exceeded | | 5 | NOT_FOUND | Not found | | 7 | PERMISSION_DENIED | Permission denied | | 14 | UNAVAILABLE | Service unavailable | | 16 | UNAUTHENTICATED | Unauthenticated |
Advanced Usage
Custom Metadata
Send custom headers with any call:
const response = await client.call(
'Service/Method',
request,
{
metadata: {
'authorization': 'Bearer token',
'x-custom-header': 'value'
}
}
);Timeout Configuration
Set custom timeouts per call:
const response = await client.call(
'Service/Method',
request,
{
timeout: 5000 // 5 seconds
}
);Connection Management
The client automatically manages gRPC channels with:
- Connection pooling
- Automatic reconnection
- Keep-alive (30s interval)
- Proper cleanup on close
File Organization
Place proto files in your app bundle:
app/
assets/
protos/
auth.proto
auth.desc
feed.proto
feed.descReference them in your code:
import * as FileSystem from 'expo-file-system';
const client = new GrpcClient({
host: 'api.example.com',
port: 50051,
protoPath: `${FileSystem.documentDirectory}protos/auth.proto`,
useTls: true,
});Complete Example
import { GrpcClient } from 'grpcx';
import * as FileSystem from 'expo-file-system';
async function example() {
// Create client
const client = new GrpcClient({
host: 'grpc.example.com',
port: 50051,
protoPath: `${FileSystem.documentDirectory}protos/service.proto`,
useTls: true,
});
try {
// Load proto
await client.load();
console.log('Services:', client.getServices());
// Unary call
const user = await client.call('users.UserService/GetUser', { id: 123 });
console.log('User:', user);
// Server streaming
const stream = client.serverStream('users.UserService/StreamUsers', {});
stream.on('data', (user) => {
console.log('User:', user);
});
stream.on('end', () => {
console.log('All users received');
});
stream.on('error', (error) => {
console.error('Stream error:', error);
});
} catch (error) {
console.error('Error:', error);
} finally {
await client.close();
}
}Platform Support
- iOS: 15.1+
- Android: API 24+ (Android 7.0+)
- React Native: 0.70+
- Expo: SDK 50+
Dependencies
Runtime Dependencies
None! This is a zero-dependency package (peer dependencies only).
Peer Dependencies
- expo
- react
- react-native
Native Dependencies
Android:
- gRPC-Java 1.65.0
- Protobuf-Java 3.25.3
iOS:
- gRPC-Swift 1.23.0
- SwiftProtobuf 1.27.0
Troubleshooting
Proto files not found
Ensure proto and desc files are in your app bundle and the path is correct:
// Check file exists
const fileInfo = await FileSystem.getInfoAsync(protoPath);
console.log('File exists:', fileInfo.exists);Connection refused
For development:
- iOS Simulator: Use
localhost - Android Emulator: Use
10.0.2.2instead oflocalhost - Physical devices: Use your computer's IP address
const client = new GrpcClient({
// For Android Emulator
host: '10.0.2.2',
port: 50051,
useTls: false, // For development
});Descriptor errors
Ensure you include imports when generating descriptors:
protoc --descriptor_set_out=service.desc --include_imports service.protoBuild errors on iOS
Run pod install:
cd ios && pod installBuild errors on Android
Clean and rebuild:
cd android && ./gradlew clean
cd .. && npx expo run:androidDevelopment
Building from source
# Clone the repository
git clone https://github.com/soorajpandey/grpcx.git
cd grpcx
# Install dependencies
npm install
# Build
npm run build
# Run example
cd example
npm install
npx expo run:iosRunning tests
npm testContributing
Contributions are welcome! Please read the contributing guidelines before submitting a PR.
Areas for contribution
- Additional documentation and examples
- Unit and integration tests
- Web support (gRPC-Web)
- Interceptor support
- Advanced retry policies
- Performance optimizations
Resources
License
MIT
Author
Sooraj Pandey
- Email: [email protected]
- GitHub: @soorajpandey
Changelog
0.1.0 (Initial Release)
- Complete gRPC client implementation
- All 4 streaming types supported
- Runtime proto loading
- TypeScript support
- iOS and Android support
- Connection management
- Error handling
Acknowledgments
- Built with Expo Modules API
- Uses gRPC-Java and gRPC-Swift
- Inspired by the need for native gRPC support in React Native
Support
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/soorajpandey/grpcx/issues
- Email: [email protected]
Made with care for the React Native community.
