A React Native/Expo module for gRPC with full streaming support (unary, client streaming, server streaming, and bidirectional streaming).
- 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
npm install @vync/grpcx
# or
yarn add @vync/grpcxcd ios && pod installNo additional steps required - Gradle dependencies are automatically handled.
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.
import { GrpcClient } from '@vync/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();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);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();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();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();// 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.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 |
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
Create a server streaming call.
Parameters:
method- Full method namerequest- Request objectoptions- Optional call options
Returns: ServerStream instance
Create a client streaming call.
Parameters:
method- Full method nameoptions- Optional call options
Returns: ClientStream instance
Create a bidirectional streaming call.
Parameters:
method- Full method nameoptions- Optional call options
Returns: BidiStream instance
Get all available services from the loaded proto.
Returns: Array of service descriptors
Get a specific service descriptor.
Parameters:
serviceName- Full service name
Returns: Service descriptor or undefined
Get a specific method descriptor.
Parameters:
fullMethodName- Full method name (e.g., 'package.Service/Method')
Returns: Method descriptor or undefined
Close the client and cleanup all resources.
Base class for all stream types. Extends EventEmitter.
data: Emitted when data is receivederror: Emitted on errorend: Emitted when stream completesmetadata: Emitted when metadata is receivedstatus: Emitted with gRPC status
Write data to the stream (client/bidi streams only).
End the stream (half-close).
Cancel the stream.
Get the stream ID.
Full TypeScript definitions are included. All types are exported from the main package:
import {
GrpcClient,
GrpcClientConfig,
GrpcMethodType,
GrpcStatusCode,
GrpcError,
ServerStream,
ClientStream,
BidiStream,
} from '@vync/grpcx';Errors follow gRPC status codes:
import { GrpcStatusCode } from '@vync/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);
}| 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 |
Send custom headers with any call:
const response = await client.call(
'Service/Method',
request,
{
metadata: {
'authorization': 'Bearer token',
'x-custom-header': 'value'
}
}
);Set custom timeouts per call:
const response = await client.call(
'Service/Method',
request,
{
timeout: 5000 // 5 seconds
}
);The client automatically manages gRPC channels with:
- Connection pooling
- Automatic reconnection
- Keep-alive (30s interval)
- Proper cleanup on close
Place proto files in your app bundle:
app/
assets/
protos/
auth.proto
auth.desc
feed.proto
feed.desc
Reference 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,
});import { GrpcClient } from '@vync/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();
}
}- iOS: 15.1+
- Android: API 24+ (Android 7.0+)
- React Native: 0.70+
- Expo: SDK 50+
None! This is a zero-dependency package (peer dependencies only).
- expo
- react
- react-native
Android:
- gRPC-Java 1.65.0
- Protobuf-Java 3.25.3
iOS:
- gRPC-Swift 1.23.0
- SwiftProtobuf 1.27.0
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);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
});Ensure you include imports when generating descriptors:
protoc --descriptor_set_out=service.desc --include_imports service.protoRun pod install:
cd ios && pod installClean and rebuild:
cd android && ./gradlew clean
cd .. && npx expo run:android# 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:iosnpm testContributions are welcome! Please read the contributing guidelines before submitting a PR.
- Additional documentation and examples
- Unit and integration tests
- Web support (gRPC-Web)
- Interceptor support
- Advanced retry policies
- Performance optimizations
MIT
Sooraj Pandey
- Email: surajpandey0016@gmail.com
- GitHub: @soorajpandey
- Complete gRPC client implementation
- All 4 streaming types supported
- Runtime proto loading
- TypeScript support
- iOS and Android support
- Connection management
- Error handling
- Built with Expo Modules API
- Uses gRPC-Java and gRPC-Swift
- Inspired by the need for native gRPC support in React Native
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/soorajpandey/grpcx/issues
- Email: surajpandey0016@gmail.com
Made with care for the React Native community.