gRPC 101 with Docker, NodeJS and Express

Raju Dawadi
3 min readApr 6, 2019

--

This is one of the simplest examples for diving into gRPC, a remote procedure call framework with the usage of Node.js gRPC API for building client and server. Our project includes Dockerfile separately for client, server and a single docker-compose file for running both of the services. But you can adjust the architecture as per your need.

Diving into …

We have a proto file for definition of service Greeter

hello.proto

syntax = "proto3";package helloworld;service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
required string name = 1;
}
message HelloReply {
required string message = 1;
}

There is a RPC method named SayHello that takesHelloRequest parameter from nodejs client and returns HelloReply from nodejs server. Here, we define hello request and hello reply message where both are string corresponding to HelloRequest and HelloReply. We define the messages as required but that could be optional. Each field in the message should have name and type. Simple as that.

On the server side

We have implemented gRPC server on server.js file inside server folder which reference proto file hello.proto and import grpc module. gRPC Protobuf Loader is used to load the proto file. We have sayHello rpc method which handles the operation request and we bind the server to port 50051.

server.js:

var PROTO_PATH = __dirname + '/hello.proto';
var grpc = require('grpc');
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
function sayHello(call, callback) {
callback(null, {message: 'Hello ' + call.request.name});
}
function main() {
var server = new grpc.Server();
server.addService(hello_proto.Greeter.service, {sayHello: sayHello});
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
server.start();
}
main();

And finally, Dockerfile for getting packages and running server

Dockerfile:

FROM node:11-alpine
COPY ./ /app/
WORKDIR /app
RUN npm install
CMD ["node", "server.js"]

Client time

Our client uses express server which handles GET request binding to port 3000.

client-express.js:

var PROTO_PATH = __dirname + '/hello.proto';var grpc = require('grpc');
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
const express = require('express')
const app = express()
const port = 3000
function main() {
var client = new hello_proto.Greeter('grpc-server:50051', grpc.credentials.createInsecure());
var user;
app.get('/:id', function (req, res) {
user = req.params.id;
client.sayHello({name: user}, function(err, response) {
console.log('Greeting:', response.message);
res.send(response.message);
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
}
main();

Dockerfile:

FROM node:11-alpine
COPY ./ /app/
WORKDIR /app
RUN npm install
CMD ["node", "client-express.js"]

Docker Compose for binding server and client

We have 2 services: grpc-server and grpc-client with container names which uses Dockerfile of respective folder for building image and running the services.

version: "3"
services:
grpc-server:
container_name: grpc-server
build:
context: ./server
dockerfile: Dockerfile
grpc-client:
container_name: grpc-client
build:
context: ./client
dockerfile: Dockerfile
ports:
- "3000:3000"

Port 3000 is mapped so that we can call the client from localhost.

Get the services running:

$ docker-compose up -d

Check the logs

$ docker-compose logs -f

Finally, send request to client

$ curl localhost:3000/raju

On the logs, we can see response

Attaching to grpc-client, grpc-server
grpc-client | Example app listening on port 3000!
grpc-client | Greeting: Hello raju

By this way we can build microservices using grpc and docker which eases the process of building applications by implementing docker.

In my next post, we will talk about the deployment of these services on Kubernetes. Stay tuned.

You can find my interesting updates on Linkedin, Twitter.

--

--

Raju Dawadi
Raju Dawadi

Responses (5)