@getezy/grpc-client
v1.0.11
Published
Universal, extendable, typesafe gRPC/gRPC-Web client for node used in ezy.
Downloads
5
Readme
🛵 Universal, extendable, typesafe gRPC/gRPC-Web client for node used in ezy.
Warning
This lib is not production ready until it is merged to main ezy project.
Installation
npm i @getezy/grpc-client
Usage
This library was designed to be extendable as much as possible.
To start you need GrpcClient
, Loader
and Protocol
.
Use GrpcClientFactory
for creating GrpcClient
instance.
GrpcClientFactory.create(loader: AbstractLoader, protocol: AbstractProtocol): Promise<GrpcClient>
import { GrpcClientFactory, GrpcProtocol, ProtobufLoader } from '@getezy/grpc-client';
import * as path from 'node:path';
const client = await GrpcClientFactory.create(
new ProtobufLoader(path.join(__dirname, '../proto/main.proto')),
new GrpcProtocol({ address: '10.10.10.10' })
);
const payload = { id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' };
const metadata = { 'x-access-token': 'token' };
const response = await client.invokeUnaryRequest<Request, Response>(
{ service: 'simple_package.v1.SimpleService', method: 'SimpleUnaryRequest' },
payload,
metadata
);
GrpcClient API
GrpcClient
has public methods to query gRPC server:
class GrpcClient<MetadataValue, Metadata> {
public invokeUnaryRequest<Request, Response>(
options: GrpcRequestOptions,
request: Request,
metadata?: Record<string, MetadataValue>
): Promise<GrpcResponse<Response>>;
public invokeClientStreamingRequest<Request, Response>(
options: GrpcRequestOptions,
metadata?: Record<string, MetadataValue>
): ClientStream<Request, Response>;
public invokeServerStreamingRequest<Request, Response>(
options: GrpcRequestOptions,
payload: Request,
metadata?: Record<string, MetadataValue>
): ServerStream<Response>;
public invokeBidirectionalStreamingRequest<Request, Response>(
options: GrpcRequestOptions,
metadata?: Record<string, MetadataValue>
): BidirectionalStream<Request, Response>;
}
The first argument in each method defines the query options - service
and method
name.
interface GrpcRequestOptions {
service: string;
method: string;
}
Response Convention
Response
type in methods are wrapped with GrpcResponse
type.
interface GrpcErrorResponseValue {
details?: string;
metadata?: Record<string, unknown>;
}
type GrpcResponseValue = Record<string, unknown>;
interface GrpcResponse<Response extends GrpcResponseValue = GrpcResponseValue> {
/**
* Status code of gRPC request
*/
code: GrpcStatus;
/**
* For unary requests - query execution time in milliseconds.
* For streaming requests - receiving response actual time in utc.
*/
timestamp: number;
data: Response | GrpcErrorResponseValue;
}
Unary Request
const response = await client.invokeUnaryRequest(
{ service: 'simple_package.v1.SimpleService', method: 'SimpleUnaryRequest' },
{ id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' }
);
Note
If error occured this method will not through error. You can handle this by checking status code in response.
import { GrpcStatus } from '@getezy/grpc-client';
const response = await client.invokeUnaryRequest(
{ service: 'simple_package.v1.SimpleService', method: 'SimpleUnaryRequest' },
{ id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' }
);
if (response.code !== GrpcStatus.OK) {
// do something
}
Client-Streaming Request
const stream = client.invokeClientStreamingRequest({
service: 'simple_package.v1.SimpleService',
method: 'SimpleClientStreamRequest',
});
stream.on('response', (response) => {});
stream.on('error', (error) => {});
stream.write({ id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' });
stream.end();
// If you want to cancel stream from the client do
stream.cancel()
ClientStream
extended from EventEmitter
.
Events:
stream.on('response', (response: GrpcResponse<Response>) => {})
Sibscribe on server response.
stream.on('error', (error: GrpcResponse<Response>) => {})
Subscribe on server error.
Methods:
stream.write(payload: Request)
Send payload to the stream.
stream.cancel()
Cancel the stream.
stream.end()
End the stream.
Server-Streaming Request
const stream = client.invokeServerStreamingRequest(
{
service: 'simple_package.v1.SimpleService',
method: 'SimpleServerStreamRequest',
},
{ id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' },
);
stream.on('response', (response) => {});
stream.on('error', (error) => {});
stream.on('end', () => {});
// If you want to cancel stream from the client do
stream.cancel()
ServerStream
extended from EventEmitter
.
Events:
stream.on('response', (response: GrpcResponse<Response>) => {})
Sibscribe on server response.
stream.on('error', (error: GrpcResponse<Response>) => {})
Subscribe on server error.
Methods:
stream.cancel()
Cancel the stream.
Bidirectional-Streaming Request
const stream = client.invokeBidirectionalStreamingRequest({
service: 'simple_package.v1.SimpleService',
method: 'SimpleBidirectionalStreamRequest',
});
stream.on('response', (response) => {});
stream.on('error', (error) => {});
stream.on('end-server-stream', () => {})
stream.write({ id: '21443e83-d6ab-45b7-9afd-65b2e0ee8957' });
stream.end();
// If you want to cancel stream from the client do
stream.cancel()
BidirectionalStream
extended from EventEmitter
.
Events:
stream.on('response', (response: GrpcResponse<Response>) => {})
Sibscribe on server response.
stream.on('error', (error: GrpcResponse<Response>) => {})
Subscribe on server error.
stream.on('end-server-stream', () => {})
Subscribe on end server stream.
Methods:
stream.write(payload: Request)
Send payload to the stream.
stream.cancel()
Cancel the stream.
stream.end()
End the client stream.
TLS
Read this article to understand how to configure TLS for gRPC and gRPC-Web server.
There are three connection types:
- Insecure — all data transmitted without encryption.
- Server-Side TLS — browser like encryption, where only the server provides TLS certificate to the client.
- Mutual TLS — most secure, both the server and the client provides their certificates to each other.
Note
Each protocol accepts TLS options. If TLS options are not specifiedInsecure
connection will be used by default.
Insecure
import { GrpcProtocol, GrpcTlsType } from '@getezy/grpc-client';
const protocol = new GrpcProtocol({
address: '10.10.10.10',
tls: {
type: GrpcTlsType.INSECURE,
},
});
Server-Side TLS
import * as path from 'node:path';
import { GrpcProtocol, GrpcTlsType } from '@getezy/grpc-client';
const protocol = new GrpcProtocol({
address: '10.10.10.10',
tls: {
tls: {
type: GrpcTlsType.SERVER_SIDE,
rootCertificatePath: path.join(__dirname, '../certs/ca-cert.pem'),
},
},
});
Note
rootCertificatePath - is optional, usually used if your server has self-signed CA
Mutual TLS
import * as path from 'node:path';
import { GrpcProtocol, GrpcTlsType } from '@getezy/grpc-client';
const protocol = new GrpcProtocol({
address: '10.10.10.10',
tls: {
type: GrpcTlsType.MUTUAL,
rootCertificatePath: path.join(__dirname, '../certs/ca-cert.pem'),
clientCertificatePath: path.join(__dirname, '../certs/client-cert.pem'),
clientKeyPath: path.join(__dirname, '../certs/client-key.pem'),
},
});
Note
rootCertificatePath - is optional, usually used if your server has self-signed CA
Loaders
Loader
- is the strategy defines how to load gRPC package definitions.
ProtobufLoader
Uses @grpc/proto-loader for load protobuf definition from the giving path.
new ProtobufLoader(path: string, [options])
import { ProtobufLoader } from '@getezy/grpc-client';
import * as path from 'node:path';
const loader = new ProtobufLoader(
path.join(__dirname, '../proto/main.proto'),
{
defaults: false,
}
);
await loader.load();
Refer to @grpc/proto-loader
documentation to see available options.
Default options are:
{
// Preserve field names. The default
// is to change them to camel case.
keepCase: true,
// Set default values on output objects.
// Defaults to false.
defaults: true,
// A list of search paths for imported .proto files.
includeDirs: [],
// The type to use to represent long values.
// Defaults to a Long object type.
longs: String,
}
ReflectionLoader
Loader by reflection API is coming soon.
CustomLoader
You can write custom loader implementation by extending AbstractLoader
class imported from @getezy/grpc-client
.
import { AbstractLoader } from '@getezy/grpc-client';
class CustomLoader extends AbstractLoader {
public async load(): Promise<void> {
// custom loader implementation
}
}
Protocols
Protocol
- is the strategy defines how to make queries to gRPC server.
GrpcProtocol
Uses @grpc/grpc-js.
new GrpcProtocol(options: GrpcProtocolOptions)
import { GrpcProtocol, GrpcTlsType } from '@getezy/grpc-client';
const protocol = new GrpcProtocol({
address: '10.10.10.10',
tls: { type: GrpcTlsType.INSECURE },
channelOptions: { sslTargetNameOverride: 'test' },
});
GrpcWebProtocol
Uses @improbable-eng/grpc-web.
Note
Official gRPC-Web implementation has problems with server-streaming responses. Read more here.
Warning
gRPC-Web protocol supports only unary and server-streaming requests, follow the streaming roadmap here. When you will try to call client or bidirectional streaming request with this protocol you will get the error.
new GrpcWebProtocol(options: AbstractProtocolOptions)
import { GrpcWebProtocol, GrpcTlsType } from '@getezy/grpc-client';
const protocol = new GrpcWebProtocol({
address: '10.10.10.10',
tls: { type: GrpcTlsType.INSECURE },
});
Custom protocol
You can write custom protocol implementation by extending AbstractProtocol
class imported from @getezy/grpc-client
.
import { AbstractProtocol } from '@getezy/grpc-client';
class CustomProtocol extends AbstractProtocol<MetadataValue, Metadata> {
// custom protocol implementation
}
License
Mozilla Public License Version 2.0