Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/bojand/grpc-caller

An improved Node.js gRPC client
https://github.com/bojand/grpc-caller

grpc hacktoberfest

Last synced: 5 days ago
JSON representation

An improved Node.js gRPC client

Awesome Lists containing this project

README

        

# grpc-caller

An improved [gRPC](http://www.grpc.io) client.

[![npm version](https://img.shields.io/npm/v/grpc-caller.svg?style=flat-square)](https://www.npmjs.com/package/grpc-caller)
[![build status](https://img.shields.io/travis/bojand/grpc-caller/master.svg?style=flat-square)](https://travis-ci.org/bojand/grpc-caller)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com)
[![License](https://img.shields.io/github/license/bojand/grpc-caller.svg?style=flat-square)](https://raw.githubusercontent.com/bojand/grpc-caller/master/LICENSE)

#### Features

* Promisifies request / response (Unary) calls if no callback is supplied
* Promisifies request stream / response calls if no callback is supplied
* Automatically converts plain javascript object to metadata in calls.
* Adds optional retry functionality to request / response (Unary) calls.
* Exposes expanded `Request` API for collecting metadata and status.

## Installation

```
$ npm install grpc-caller
```

## Overview

#### Improved unary calls

Works as standard gRPC client:

```js
const caller = require('grpc-caller')
const PROTO_PATH = path.resolve(__dirname, './protos/helloworld.proto')
const client = caller('0.0.0.0:50051', PROTO_PATH, 'Greeter')
client.sayHello({ name: 'Bob' }, (err, res) => {
console.log(res)
})
```

For unary calls, also promisified if callback is not provided:

```js
client.sayHello({ name: 'Bob' })
.then(res => console.log(res))
```

Which means means you can use is with `async / await`

```js
const res = await client.sayHello({ name: 'Bob' })
console.log(res)
```

For Unary calls we expose `retry` option identical to [async.retry](http://caolan.github.io/async/docs.html#retry).

```
const res = await client.sayHello({ name: 'Bob' }, {}, { retry: 3 })
console.log(res)
```

#### Improved request stream / response calls

Lets say we have a remote call `writeStuff` that accepts a stream of messages
and returns some result based on processing of the stream input.

Works as standard gRPC client:

```js
const call = client.writeStuff((err, res) => {
if (err) console.error(err)
console.log(res)
})

// ... write stuff to call
```

If no callback is provided we promisify the call such that it returns an **object
with two properties** `call` and `res` such that:

* `call` - the standard stream to write to as returned normally by grpc
* `res` - a promise that's resolved / rejected when the call is finished, in place of the callback.

Using destructuring we can do something like:

```js
const { call, res } = client.writeStuff()
res
.then(res => console.log(res))
.catch(err => console.error(err))

// ... write stuff to call
```

This means we can abstract the whole operation into a nicer promise returning
async function to use with `async / await`

```js
async function writeStuff() {
const { call, res } = client.writeStuff()
// ... write stuff to call
return res
}

const res = await writeStuff()
console.log(res)
```

#### Automatic `Metadata` creation

All standard gRPC client calls accept [`Metadata`](http://www.grpc.io/grpc/node/module-src_metadata-Metadata.html)
as first or second parameter (depending on the call type). However one has to
manually create the Metadata object. This module uses
[grpc-create-metadata](https://www.github.com/bojand/grpc-create-metadata)
to automatically create Metadata if plain Javascript object is passed in.

```js
// the 2nd parameter will automatically be converted to gRPC Metadata and
// included in the request
const res = await client.sayHello({ name: 'Bob' }, { requestid: 'my-request-id-123' })
console.log(res)
```

We can still pass an actual `Metadata` object and it will be used as is:

```js
const meta = new grpc.Metadata()
meta.add('requestid', 'my-request-id-123')
const res = await client.sayHello({ name: 'Bob' }, meta)
console.log(res)
```

## Request API

In addition to simple API above, the library provides a more detailed `"Request"` API that can
be used to control the call details. The API can only be used for Unary and
request streaming calls.

#### Unary calls

```js
const req = new client
.Request('sayHello', { name: 'Bob' }) // call method name and argument
.withMetadata({ requestId: 'bar-123' }) // call request metadata
.withResponseMetadata(true) // we want to collect response metadata
.withResponseStatus(true) // we want to collect the response status
.withRetry(5) // retry options

const res = await req.exec()
// res is an instance of our `Response`
// we can also call exec() using a callback

console.log(res.response) // the actual response data { message: 'Hello Bob!' }
console.log(res.metadata) // the response metadata
console.log(res.status) // the response status
console.log(res.call) // the internal gRPC call
```

#### Request streaming calls

In case of request streaming calls if `exec()` is called with a callback the gRPC `call` stream is returned.
If no callback is provided an object is returned with `call` property being the call stream and `res`
property being a Promise fulfilled when the call is completed. There is no `retry` option for
request streaming calls.

```js

const req = new client.Request('writeStuff') // the call method name
.withMetadata({ requestId: 'bar-123' }) // the call request metadata
.withResponseMetadata(true) // we want to collect response metadata
.withResponseStatus(true) // we want to collect the response status

const { call, res: resPromise } = req.exec()

// ... write data to call

const res = await resPromise // res is our `Response`

console.log(res.response) // the actual response data
console.log(res.metadata) // the response metadata
console.log(res.status) // the response status
console.log(res.call) // the internal gRPC call
```

## API Reference

### Request
A Request class that encapsulates the request of a call.

**Kind**: global class

* [Request](#Request)
* [new Request(methodName, param)](#new_Request_new)
* [.withGrpcOptions(opts)](#Request+withGrpcOptions) ⇒ Object
* [.withMetadata(opts)](#Request+withMetadata) ⇒ Object
* [.withRetry(retry)](#Request+withRetry) ⇒ Object
* [.withResponseMetadata(value)](#Request+withResponseMetadata) ⇒ Object
* [.withResponseStatus(value)](#Request+withResponseStatus) ⇒ Object
* [.exec(fn)](#Request+exec) ⇒ Promise \| Object

#### new Request(methodName, param)
Creates a Request instance.

| Param | Type | Description |
| --- | --- | --- |
| methodName | String | the method name. |
| param | \* | the call argument in case of `UNARY` calls. |

#### request.withGrpcOptions(opts) ⇒ Object
Create a request with call options.

**Kind**: instance method of [Request](#Request)
**Returns**: Object - the request instance.

| Param | Type | Description |
| --- | --- | --- |
| opts | Object | The gRPC call options. |

#### request.withMetadata(opts) ⇒ Object
Create a request with call metadata.

**Kind**: instance method of [Request](#Request)
**Returns**: Object - the request instance.

| Param | Type | Description |
| --- | --- | --- |
| opts | Object | The gRPC call metadata. Can either be a plain object or an instance of `grpc.Metadata`. |

#### request.withRetry(retry) ⇒ Object
Create a request with retry options.

**Kind**: instance method of [Request](#Request)
**Returns**: Object - the request instance.

| Param | Type | Description |
| --- | --- | --- |
| retry | Number \| Object | The retry options. Identical to `async.retry`. |

#### request.withResponseMetadata(value) ⇒ Object
Create a request indicating whether we want to collect the response metadata.

**Kind**: instance method of [Request](#Request)
**Returns**: Object - the request instance.

| Param | Type | Description |
| --- | --- | --- |
| value | Boolean | `true` to collect the response metadata. Default `false`. |

#### request.withResponseStatus(value) ⇒ Object
Create a request indicating whether we want to collect the response status metadata.

**Kind**: instance method of [Request](#Request)
**Returns**: Object - the request instance.

| Param | Type | Description |
| --- | --- | --- |
| value | Boolean | `true` to collect the response status metadata. Default `false`. |

#### request.exec(fn) ⇒ Promise \| Object
Execute the request.

**Kind**: instance method of [Request](#Request)
**Returns**: Promise \| Object - If no callback is provided in case of `UNARY` call a Promise is returned.
If no callback is provided in case of `REQUEST_STREAMING` call an object is
returned with `call` property being the call stream and `res`
property being a Promise fulfilled when the call is completed.

| Param | Type | Description |
| --- | --- | --- |
| fn | function | Optional callback |

### Response
A Response class that encapsulates the response of a call using the `Request` API.

**Kind**: global class

* [Response](#Response)
* [.call](#Response+call) : Object
* [.response](#Response+response) : Object
* [.metadata](#Response+metadata) : Object
* [.status](#Response+status) : Object

#### response.call : Object
The response's gRPC call.

**Kind**: instance property of [Response](#Response)

#### response.response : Object
The actual response data from the call.

**Kind**: instance property of [Response](#Response)

#### response.metadata : Object
The response metadata.

**Kind**: instance property of [Response](#Response)

#### response.status : Object
The response status metadata.

**Kind**: instance property of [Response](#Response)

### caller(host, proto, name, credentials, options, defaults) ⇒ Object
Create client isntance.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| host | String | The host to connect to |
| proto | String \| Object | Path to the protocol buffer definition file or Object specifying file to load and load options for proto loader. |
| name | String | In case of proto path the name of the service as defined in the proto definition. |
| credentials | Object | The credentials to use to connect. Defaults to `grpc.credentials.createInsecure()` |
| options | Object | Options to be passed to the gRPC client constructor |
| options.retry | Object | In addition to gRPC client constructor options, we accept a `retry` option. The retry option is identical to `async.retry` and is passed as is to it. This is used only for `UNARY` calls to add automatic retry capability. |
| defaults | Object | Metadata and Options that will be passed to every Request |

**Example** *(Create client dynamically)*
```js
const PROTO_PATH = path.resolve(__dirname, './protos/helloworld.proto')
const client = caller('localhost:50051', PROTO_PATH, 'Greeter')
```
**Example** *(With options)*
```js
const file = path.join(__dirname, 'helloworld.proto')
const load = {
// ... proto-loader load options
}
const client = caller('localhost:50051', { file, load }, 'Greeter')
```
**Example** *(Create a static client)*
```js
const services = require('./static/helloworld_grpc_pb')
const client = caller('localhost:50051', services.GreeterClient)
```
**Example** *(Pass Options, Default Metadata and Interceptor options)*
```js
const metadata = { node_id: process.env.CLUSTER_NODE_ID };
const credentials = grpc.credentials.createInsecure()
const options = {
interceptors = [ bestInterceptorEver ]
}
const client = caller('localhost:50051', PROTO_PATH, 'Greeter', credentials, options, {
metadata: { foo: 'bar' }
})

// Now every call with that client will result
// in invoking the interceptor and sending the default metadata
```

* [caller(host, proto, name, credentials, options, defaults)](#caller) ⇒ Object
* [.metadata](#caller.metadata)
* [.wrap](#caller.wrap)

#### caller.metadata
Utility helper function to create Metadata object from plain Javascript object.
See grpc-create-metadata module.

**Kind**: static property of [caller](#caller)

#### caller.wrap
Utility function that can be used to wrap an already constructed client instance.

**Kind**: static property of [caller](#caller)
## License

Apache-2.0