Client
The clients factory in gRPCity discovers services from the loader, dials them, and hands you a typed proxy you can call like an ordinary async function.
Initialization
const clients = await loader.initClients({ services, channelOptions, credentials })Parameters:
services(required) —{ [serviceName]: address }serviceName: fully qualified service name, including the package.address: server address. Eitherhost:port(string) or{ host, port }.
channelOptions(optional) — channel-level configuration passed through to gRPC.credentials(optional) — client credentials, e.g. for TLS.
Example:
const credentials = loader.makeClientCredentials(rootCerts, privateKey, certChain, verifyOptions)
const clients = await loader.initClients({
services: {
'dev.path.to.serviceA': 'domain.local:9099',
'path.to.serviceB': {
host: '192.168.32.111',
port: 10099
}
},
channelOptions,
credentials
})Middleware
clients.use() runs around every RPC, before and after the call. See the Client Middleware guide.
// add one at a time
clients.use(f1)
clients.use(f2)
clients.use(f3)
// or pass several positional middlewares
clients.use(f1, f2, f3)
// or pass an array
clients.use([f1, f2, f3])Middleware only runs around async calls. The callback form bypasses it.
Getting an instance
get(name) returns a cached client proxy for the given fully qualified service name.
const serviceAClient = clients.get('dev.path.to.serviceA')
const serviceBClient = clients.get('path.to.serviceB')get() caches by name, so repeated calls return the same instance. Use getReal() if you need a fresh, uncached client.
Making an RPC call
Two styles are supported: async and callback. The async form is the default.
async
const result = await serviceAClient.rpcMethod(request, metadata, options)Arguments:
rpcMethod— the method declared on the service in your.proto. There are four kinds: unary, client streaming, server streaming, and bidi streaming.request— the request payload, shaped after the message type from the proto.metadata(optional) — request metadata as key/value pairs (auth, tracing, etc.).options(optional) — per-call options. Recognised fields includetimeout(per-call deadline; defaults to 10 seconds when omitted) andsignal(anAbortSignalto cancel the call — see AbortSignal).
Unary example:
const meta = loader.makeMetadata({
'x-business-id': ['grpcity', 'testing'],
'x-timestamp-client': 'begin=' + new Date().toISOString()
})
const options = {
timeout: 5000 // 5 seconds from now
}
const { status, peer, metadata, response } = await serviceAClient.rpcMethod(
{ name: 'myapp' },
meta,
options
)Every call resolves to four fields:
status— the gRPC status returned by the server.peer— the remote address you talked to.metadata— trailing metadata returned by the server.response— the response payload itself.
A typical result looks like:
{
status: {
code: 0,
details: 'OK',
metadata: Metadata { internalRepr: Map(0) {}, options: {} }
},
peer: '::9098',
metadata: Metadata {
internalRepr: Map(2) { 'content-type' => [Array], 'date' => [Array] },
options: {}
},
response: { message: 'hello greeter by Greeter in server1', count: 1 }
}callback
The callback form sits behind .call.:
const a = serviceAClient.call.rpcMethod1({ name: 'myapp' }, (err, result) => {
if (err) throw err
return result
})
const b = serviceBClient.call.rpcMethod2({ name: 'youapp' }, (err, result) => {
if (err) throw err
return result
})Reinitialization
When server addresses change at runtime, clear the existing clients first, then re-init with the new mapping.
const clients = await loader.initClients({
services: {
'dev.path.to.serviceA': 'domain.local:9099',
'path.to.serviceB': { host: '192.168.32.111', port: 10099 }
}
})
clients.clear()
clients.init({
services: {
'dev.path.to.serviceA': 'domain.local:9088',
'path.to.serviceB': { host: '192.168.32.112', port: 10099 }
}
})clear() must run before init(). Otherwise the new init returns the cached factory and the address swap silently has no effect.