gRPC + NodeJS = Chat Example

In this article we are going to create a different chat using NodeJS and gRPC. But before we start, if you don’t know what is gRPC, I strongly recommend you to check this Jesus’s post about the challenges of our API architecture and gRPC concepts and also read this another post from Daniel explaining the idea of gRPC with Angular.

Ok, If you are here, I suppose that now you are an expert on gRPC and we can discuss better. In my opinion, one of the best benefits (of RPC in general) is the possibility to call remote functions as we are local and is fast, REALLY fast. We know that exists many ways to do remote calls, I find this article very good and fun making some comparisons between most of them. If you still want to know why gRPC is a good option over REST, check some references at the end of this article.

Where we going to use it?

If you are following the my lastest posts at Fexco Tech Blog (here and here) , I’m explaining some parts of the Front end Architecture’s idea. And now we are implementing it. In the last post (part 2), I could explain the idea of FOG and EDGE and how they will communicate with each other: The Edge layer will communicate with the Fog Layer only through the CouchDB synchronization. But who is populating the CouchDB? That’s the job of the powerful Cloud Keeper!

Somehow, the Cloud Keeper needs to communicate with the Cloud API, this could be over REST, Socket, or others. So we are going to use gRPC at the FOG level to create a stream with Cloud.

Why?

For many reasons, but mainly because it’s fast! Really Fast! And save us a lot of network traffic. gRPC is built on top of HTTP/2, which allows for client-side and/or server-side streaming. You can run this example where they download 200 small images to compare the difference between HTTP/1.1 and HTTP/2:

http://www.http2demo.io/ (200 small images being rendered)

There are more comparison and benchmarks over the whole internet, if you need to see more, check the references at the end.

Finally! Lets see some code!

We all love to chat, now we are going to chat using a much … much … more fun way, using the Command line MuaHaHa (evil laugh). This is what we are going to do:

You can start a regular node Project, if you want the final code, check it here. If you want to do everything by yourself, great, start by this following commands:

# Init an empty project, Answer whatever you want, and it will create package.json
$ npm init

# Install grpc libraries
$ npm install --save grpc @grpc/proto-loader

Ok ready to go! Next you will need to create the project code. It has only 3 files:

  • chat.proto: Our data structure, defining our functions and messages.
  • server.js: Our gRPC Server
  • client.js: Our gRPC Client

Chat.proto

The Chat structure will be defined using the following protobuf, create this file inside the folder protos/chat.proto:

	syntax = "proto3"; //Specify proto3 version.

	package example; //Optional: unique package name.

	service Chat { //Service class to be used by the clients
		rpc join(stream Message) returns (stream Message){}
		rpc send(Message) returns (Message){}
	}

	message Message { //Information that will be passed between client and service
		string user = 1;
		string text = 2;
	}

First we defined a service and its functions that will be used by the client. Each function receives and returns some message. We defined two functions:

  • join: Receive a stream message and return the same stream message.
  • send: Just receive a message and respond with another message.

Just for the purpose to simplify this example we are using the same message as argument and return, but it is not mandatory, you can define a different argument or return.

The message that will be transported between the client and service was creatively called “Message” with the name of the user and the text message.

Server.js

	let grpc = require("grpc");
	var protoLoader = require("@grpc/proto-loader");

	const server = new grpc.Server();
	const SERVER_ADDRESS = "0.0.0.0:5001";

	// Load protobuf
	let proto = grpc.loadPackageDefinition(
	  protoLoader.loadSync("protos/chat.proto", {
		keepCase: true,
		longs: String,
		enums: String,
		defaults: true,
		oneofs: true
	  })
	);

	let users = [];

	// Receive message from client joining
	function join(call, callback) {
	  users.push(call);
	  notifyChat({ user: "Server", text: "new user joined ..." });
	}

	// Receive message from client
	function send(call, callback) {
	  notifyChat(call.request);
	}

	// Send message to all connected clients
	function notifyChat(message) {
	  users.forEach(user => {
		user.write(message);
	  });
	}

	// Define server with the methods and start it
	server.addService(proto.example.Chat.service, { join: join, send: send });

	server.bind(SERVER_ADDRESS, grpc.ServerCredentials.createInsecure());

	server.start();


The idea of this server is a simpler sender. Every time that a client join the chat, we save his instance on the users list so they all can be notified every time that something happens in the chat. The Send function is just to receive the message from some user and broadcast to everyone in the chat. In the final, we add our service to the server and start it.

To simplify this example, we are using an insecure connection. But gRPC uses a secured stream connection with SSL/TLS, Token-based authentication with Google. You can read more about it here.

Client.js

	let grpc = require("grpc");
	var protoLoader = require("@grpc/proto-loader");
	var readline = require("readline");

	//Read terminal Lines
	var rl = readline.createInterface({
	  input: process.stdin,
	  output: process.stdout
	});

	//Load the protobuf
	var proto = grpc.loadPackageDefinition(
	  protoLoader.loadSync("protos/chat.proto", {
		keepCase: true,
		longs: String,
		enums: String,
		defaults: true,
		oneofs: true
	  })
	);

	const REMOTE_SERVER = "0.0.0.0:5001";

	let username;

	//Create gRPC client
	let client = new proto.example.Chat(
	  REMOTE_SERVER,
	  grpc.credentials.createInsecure()
	);

	//Start the stream between server and client
	function startChat() {
	  let channel = client.join({ user: username });

	  channel.on("data", onData);

	  rl.on("line", function(text) {
		client.send({ user: username, text: text }, res => {});
	  });
	}

	//When server send a message
	function onData(message) {
	  if (message.user == username) {
		return;
	  }
	  console.log(`${message.user}: ${message.text}`);
	}

	//Ask user name then start the chat
	rl.question("What's ur name? ", answer => {
	  username = answer;

	  startChat();
	});

The client has a little bit more work. Users are interacting and sending messages through command line so we are reading this lines using the readline (default NodeJS package).

To communicate with the server we always need to load the protobuf (is the same used by server), then we can call and receive messages from server. For this example, first we ask the User’s name and after his answer we start the chat.

As any conventional chat, user first joins to a channel sending his username. The server will receive this user and save in the users list. Every time that something is typed on the command line, we call the function send passing the typed text to the server broadcast it for every user in this chat.

Running

All set? If you want to get the full code, get it here. Now lets run this! Now you going to need different terminals for each following command (execute it the project’s root folder):

//Start server
$ node server

//Start client
$ node client

//Start another client
$ node client

And that’s it! Have fun!

Summary

gRPC is an excellent promise to make the client-server communication faster and secure. It is always important to consider the project requirements checking if the it is a good solution for your project. Cases like where you need a really FAST, compacted and secure communication between your server and its clients, I think gRPC will fits like a glove.

References

Tulio Castro

Author: Tulio Castro

Fexco Senior Software Engineer

One Reply to “gRPC + NodeJS = Chat Example”

  1. I really don’t know how, but I’m glad to be the first for comment.

    You’ve nailed it! the best written, awesome detailed, and dummy-proof explained.

    Probably covered all the question I had about this gRPC.

    Thank you Tulio.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.