This time, I want to share about creating Node.js microservices project using gRPC as the method of communication between the services.
“Do everything quickly and well.”
― G.I. Gurdjieff
Introduction
Hey, it’s been a while since the last post. Been busy writing articles for the company I work at.
This time, I would like to share about implementing gRPC for communications between microservices in Node.js. Do you know about it?
What is gRPC?
To quote from the official website of gRPC:
gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
https://grpc.io/
As implied in its name, gRPC uses RPC concepts. But you don’t really need to know how gRPC does it, because the RPC is already mapped to HTTP. So you only need to learn gRPC to use it. Oh, the “g” is for Google.
gRPC uses HTTP/2, which is the improved version to the older protocol HTTP/1.1. It uses a binary payload that is efficient to create and parse, and utilises the HTTP/2 for efficient management of connections. Though, it does not mean it can’t be done without gRPC. You would have to learn more about the tech to do this on your own.
Learning gRPC means that you must understand the concept of protocol buffers. It has a strongly-typed and binary-based format, which is more efficient and introduces type safety, unlike JSON which has a loosely-typed and test-based format. Protocol buffers are more efficient because it reduces the transmitted payload size, which could also reduce bandwidth cost in scale.
Google has been using the underlying technologies and concepts of gRPC for a long time for its cloud products and externally facing APIs. Not just Google, others such as Square, Netflix, CoreOS, Docker, CockroachDB, Cisco, Juniper Networks also use gRPC. By reading this list only, you can be sure that gRPC has been proven and is worthy to be used.
Prerequisites
We will be using these projects as a base for modification:
- This mock microservice, which connections and operations will be added for gRPC, so you can see the difference of development easily between using Express and gRPC.
Create a gRPC Server
You can use this mock microservice as basis, but it is recommended to create a new directory inside src for better file management. So, create src/grpc first.
Create a proto file
First, we are going to create Protocol Buffers. To put it simply, protocol buffers are based on the idea of defined structure. The protocol buffers in gRPC utilise the .proto
files. Here is a snippet of file src/grpc/protos/users.proto
:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.grpc.nodejs.cil.users";
option java_outer_classname = "UsersProto";
option objc_class_prefix = "USERS";
package users;
service Users {
rpc getUser (GetUserRequest) returns (GetUserReply) {}
}
message GetUserRequest {
string id = 1;
}
message GetUserReply {
string id = 1;
string name = 2;
string bankId = 3;
string amount = 4;
}
First we declare that we are going to use proto3
version of the Protocol Buffer language. Then, we set some options such as java_multiple_files
, java_package
, java_outer_classname
, and objc_class_prefix
.
After that, set the package name as users
. This is optional but it’s better to use it to avoid name clashes.
And then, we define the necessary CRUD for the operations. We will put in inside the service
block. We declare the service name as Users
. This will be used in the gRPC server later on.
Inside the service block, we create getUser
as part of “R” or “read” in CRUD. We define the function as rpc
, then the function called getUser
with a parameter GetUserRequest
which will return an output GetUserReply
.
Next, we need to define the schemas for the parameter GetUserRequest
and output GetUserReply
. Both of them are defined with the message
block. Inside the respective blocks, define what properties will be part of the schema.
For GetUserRequest
, there is only one field: id
with the type of string. There are many types of these fields beside string, and it’s called scalars by the way. You can check the rest of them here.
For GetUserReply
, we need four fields: id
, name
, bankId
, and amount
. Coincidentally, each of them also has the scalar value of string.
Lastly, for the other functions, you can add them the same way. The details will be included in the source code I share at the end of the post.
Create gRPC Server
Next, after creating the schemas, we will create the script for starting the server.
There are some libraries you have to install using npm install <library>
, defined below:
@grpc/grpc-js
@grpc/proto-loader
google-protobuf
You can find out more what each of these libraries do in the npm website.
package.json
This is the same package.json
as before, so you don’t have to create a new one. Modify the scripts
property just as below:
{
"name": "cil-nodejs-grpc",
...
"scripts": {
...
"start:grpc-server": "node ./src/grpc/server.js",
"start:grpc-client": "node ./src/grpc/client.js"
},
...
}
The script will enable you to run npm run start:grpc-server
, to differentiate from the usual npm start
which will run the Express server from before.
Code src/grpc/server.js
This file is the entry point for starting the gRPC server.
Create gRPC Server
const grpc = require('@grpc/grpc-js');
const host = '0.0.0.0';
const port = '50051';
/**
* Starts an RPC server
*/
const main = () => {
const server = new grpc.Server();
// Add gRPC services here...
console.log(`Starting server at ${host}:${port} ...`);
server.bindAsync(`${host}:${port}`, grpc.ServerCredentials.createInsecure(), () => {
server.start();
console.log(`Server started!`);
});
}
main();
Let’s create the server initiation code.
First, we load the libraries @grpc/grpc-js
for creating the gRPC server code. Next, create the host
and port
variable to define the host of the server.
Then, create a function main()
as the starting point of the server. Inside the function, create a new server by using new grpc.Server()
.
Before starting the server, we need to bind it with the defined host and port. The first argument is port
, the second argument is creds
. For port
, we assign <host>:<port>
. Then, for creds
, we assign grpc.ServerCredentials.createInsecure()
, to create insecure object credentials because we are using the insecure port.
For the third argument, we assign a callback function to run when the port is bound successfully. Inside the function, we now can start the server with server.start()
.
Load proto file
After initiating the server, now we will load the usersProto
file we created before. See this snippet below:
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const host = '0.0.0.0';
const port = '50051';
const PROTO_PATH = __dirname + '/protos/users.proto';
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const usersProto = grpc
.loadPackageDefinition(packageDefinition).users;
/**
* Starts an RPC server
*/
const main = () => {...}
main();
The first step is to load the @grpc/proto-loader
library. Then define the proto file path as you can see in PROTO_PATH
.
Next, we will load the file as packageDefinition
with protoLoader.loadSync()
. The first argument is path
, the second argument is options
. You can see more about the options here.
Lastly, define usersProto
from the packageDefinition
, using grpc.loadPackageDefinition()
. Remember to call the object users
after loading the package.
Create Handlers
We will now create handlers for handling the CRUD operations, but in gRPC style. The handler functions has the same logic with the src/handlers
counterpart, but with some modifications:
src/grpc/handlers/usersHandler.js
See this snippet below:
const getUser = (db) => {
return (call, callback) => {
const { id } = call.request;
callback(null, db.users.get(id));
}
}
...
const createUsersHandler = (db) => {
return {
getUser: getUser(db)
...
}
}
module.exports = {
createUsersHandler
};
A gRPC handler has two parameters: call
and callback
. It’s similar to Express’ req
and res
.
See the function getUser()
, the return
block part, call
has the property request
, and the request
has id
. Where is this id
coming from? Remember the users.proto
file:
// users.proto
...
service Users {
rpc getUser (GetUserRequest) returns (GetUserReply) {}
}
message GetUserRequest {
string id = 1;
}
message GetUserReply {
string id = 1;
string name = 2;
string bankId = 3;
string amount = 4;
}
Yes, the id
is from GetUserRequest
!
To return the response, use the callback
function. This function is kind of like Express’ res.send
. For now, you can set the first argument as null
, and for the second argument db.users.get(id)
because we want to return the data from the mock database as the response. Make sure it has the same schema as GetUserReply
from users.proto
!
Next question is, how can we actually assign db
to the handler? We need to do some currying. We will create the handler function by assigning db
so it an be used in the function. Which is why createUser()
has db
as a parameter. The function itself will return a function which will be assigned as the real getUser
.
To do this, we create a createUsersHandler()
function, whose job is to create the handler functions with the db
argument. This function will return an object of handlers.
Lastly, export the module with module.exports
.
src/grpc/handlers/index.js
Next, we create an index file for the gRPC handlers:
// src/grpc/handlers/index.js
const createUsersHandler = require('./usersHandler');
module.exports = {
createUsersHandler
};
First, import all handlers that will be used. There’s only one createUsersHandler
here for now, but you will already know where to add more handlers in the future.
Export this module so it can be used in src/grpc/server.js
.
SRC/GRPC/server.JS
Back to the server file. We need to connect the created handlers, so it can be used correctly. See this snippet below:
...
const protoLoader = require('@grpc/proto-loader');
const db = require('../db');
const { createUsersHandler } = require('./handlers');
...
/**
* Starts an RPC server
*/
const main = () => {
const server = new grpc.Server();
server.addService(usersProto.Users.service,
createUsersHandler(db));
...
}
main();
If you remember from the Express tutorial before, we already created a mock DB server. We are going to reuse that model, so load the module.
In the main()
function, assign the handlers in server.addService()
. The first argument of this function is service
, so put usersProto.Users.service
. And the second argument is implementation
, and it’s to be filled with the handlers from createUsersHandler(db)
.
Initiate and Start Server
Alright, the server should work now. Try to run npm run start:grpc-server
. It should show something like this:

Create a gRPC Client
The server started, but how do we know that it really works? So, let’s make a simple gRPC client to test the functions. And we will do it using the same project as the server.
File protos/users.proto
We can use the same proto file with the server one.
File src/grpc/client.js
Take a look on this snippet:
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const PROTO_PATH = __dirname + '/protos/users.proto';
const server = {
host: 'localhost',
port: '50051'
};
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const usersProto = grpc
.loadPackageDefinition(packageDefinition).users;
const main = () => {
const { host, port } = server;
const client = new usersProto.Users(
`${host}:${port}`,
grpc.credentials.createInsecure());
client.getUser(
{ id: '<user_id>' },
(err, response) => {
console.log(err || response);
});
};
}
main();
First, we load the library @grpc/grpc-js
and @grpc/proto-loader
. And then define the path to the usersProto
file we also use for the server as PROTO_PATH
. Remember to also define the target server to connect to in server
.
Next, also the same with the server code, we load the packageDefinition
with protoLoader.loadSync()
. Define usersProto
using the statement grpc
.
.loadPackageDefinition(packageDefinition).users
For the main()
function, first create the client
by using new usersProto.users()
. It needs two arguments. The first is the target server which would be filled by host
and port
, and the second is creds
, which would be filled with grpc.ServerCredentials.createInsecure()
.
After defining the client
, we can use the function we defined from usersProto
. We can now use getUser()
, but it needs two arguments. The first is the defined payload, which only need id
, and is an object. The second is the callback function which has two parameters: error
and response
. Should getUser()
returns successfully, error
will be null
and response
will have the data we need. But if it fails, response
will be undefined
, and error
will have the message of why it fails.
Test the Code
Alright, let’s test the code. Open two terminals and run npm run start:grpc-server
on one, and npm run start:grpc-client
on the other.
If everything goes well, it should be show something like this for the grpc-client:

Successfully getUser() with gRPC!
Conclusion
gRPC has made it easier for us to implement RPC in our code. The implementation is quite simple, and you don’t really have to know what it does in the background. Still, it’s better to learn about it too.
As practice, you can also add the other operations such as create, update, and delete. The mock database is already prepared in this source code I promised you earlier.
Well, I think that’s all! I hope this helps you in initiating a gRPC server and client for your projects!
See you on the next post!
“Do not compete with others! Pick a high speed for yourself that suits high ideals and try to catch and surpass that speed!”
― Mehmet Murat ildan