Giter Club home page Giter Club logo

grpc-web-client-universal's Introduction

๐Ÿ›ต Universal, extendable, typesafe gRPC/gRPC-Web client for node used in ezy.

npm npm Codacy Badge Codacy Badge

esm cjs

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 specified Insecure 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

grpc-web-client-universal's People

Contributors

notmedia avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.