Skip to main content
Version: Next

Examples

HelloWorld example

In this example, we will try to define a basic service using proto buffers, register it using the service client and calling it by its route.

Proto file definition:

helloworld.proto
syntax="proto3";

package my.package.v1;

service HelloWorld {
rpc addOne(Request) returns (Response) {}
}

message Request {
int32 value = 1;
}

message Response {
int32 value = 1;
}

Callee (server):

callee.js
const path = require('path')

const { ServiceClient } = require('hera-grpc')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

sc.registerService({
routes: '/example-1/dev~service_route/helloworld',
serviceName: 'my.package.v1.HelloWorld',
filename: path.join(__dirname, 'helloworld.proto'),
handlers: {
addOne: (call, callback) => {
const { request } = call
callback(null, { value: request.value + 1 })
},
},
})

const run = async () => {
await sc.connect()
}

run().catch(console.error)

this will register the HelloWorld service for route /example-1/dev~service_route/helloworld and start the grpc server on available IPv4 public interface with random available port.

Registered event

The service client will emit registered event when it's ready to handle requests - for more details check events.

Caller (client):

Client can now call the service by its route /example-1/dev~service_route/helloworld:

caller.js
const { assert } = require('console')
const promisify = require('util').promisify

const { ServiceClient } = require('hera-grpc')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

const run = async () => {
await sc.connect()

// Get stub with the rpc methods for the specified route
const stub = await sc.getStub('/example-1/dev~service_route/helloworld')
stub.addOne = promisify(stub.addOne)

const request = { value: 41 }
const response = await stub.addOne(request)
stub.close()

assert(response.value === 42)
}

run().catch(console.error).finally(sc.close)
Closing client

Always make sure you close the client before terminating the process, so it can properly disconnect from the network.

Propagated (or nested) calls

This example shows how to call another service within a server rpc handler. You can access getStub from any handlers Call object.

Lets modify the HelloWorld example from before, the previous service with route /example-1/dev~service_route/helloworld will become "lazy" and call another service to process the request:

Lazy Callee (server1):

callee-lazy.js
const path = require('path')
const promisify = require('util').promisify

const { ServiceClient } = require('hera-grpc')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

sc.registerService({
routes: '/example-1/dev~service_route/helloworld',
serviceName: 'my.package.v1.HelloWorld',
filename: path.join(__dirname, 'helloworld.proto'),
handlers: {
addOne: async (call, callback) => {
const { request } = call

// this service is lazy... it will pass the work to another one!
const stub = await call.getStub('/hardworking-service')
stub.addOne = promisify(stub.addOne)

const response = await stub.addOne({ value: request.value })

callback(null, { value: response.value })
},
},
})

const run = async () => {
await sc.connect()
}

run().catch(console.error)

Hard Working Callee (server2):

The following service with route /hardworking-service will actually perform the addition and return it back to the first service handler:

callee-worker.js
const path = require('path')

const { ServiceClient } = require('hera-grpc')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

sc.registerService({
routes: '/hardworking-service',
serviceName: 'my.package.v1.HelloWorld',
filename: path.join(__dirname, 'helloworld.proto'),
handlers: {
addOne: async (call, callback) => {
const { request } = call
callback(null, { value: request.value + 1 })
},
},
})

const run = async () => {
await sc.connect()
}

run().catch(console.error)

As you can see, this "hard-working" service is identical to the one from the HelloWorld example—the only difference is the changed route.

Caller (client):

Caller is identical to the one from the HelloWorld example.

POI service example

In this example, we will try to register and call the demo service from the Node gRPC Basics tutorial:

Callee (server):

callee.js
const path = require('path')

const { ServiceClient } = require('hera-grpc')

// Handlers from the POI service tutorial
const poiHandlers = require('../../poi/handlers')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

sc.registerService({
routes: '/example-1/dev~service_route/poi',
serviceName: 'RouteGuide',
handlers: poiHandlers,
filename: path.join(__dirname, '../../../proto/poi/poi.proto'),
loadOptions: {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
},
})

const run = async () => {
await sc.connect()
}

run().catch(console.error)

this will register the poi service for route /example-1/dev~service_route/poi.

Caller (client):

caller.js
const { ServiceClient } = require('hera-grpc')

const poiClient = require('../../poi/client')

const sc = new ServiceClient({
zk: 'zk://localhost:2181/hera-grpc',
})

const run = async () => {
await sc.connect()
// Get stub with the rpc methods for the specified route
const stub = await sc.getStub('/example-1/dev~service_route/poi')

// Client from the tutorial
await poiClient(stub)
stub.close()
}

run().catch(console.error).finally(sc.close)