DEV Community

Andrea Olivato
Andrea Olivato

Posted on • Originally published at andreaolivato.Medium

Websocket and Protobuf integration in Javascript

WebSockets are incredibly performing when you need a continuous dialogue between the Frontend and the Backend of an application.

If you combine them with Protobuf to structure the message exchange, you have a great combination for near-realtime data exchange.

I’ve recently found myself in need to integrate a remote WebSocket with Protobuf directly in the frontend, without using a backend to elaborate the data, in order to be faster and avoid server load.

While the integration was quite easy, I had troubles finding a good documented tutorial from start to end for frontend (no Node.JS or similar), so here we are.

Libraries and Dependancies

I am using the vanilla implementation of WebSocket in Javascript, without any external libraries, so nothing to include there.

We only need a Protobuf implementation in Javascript, and my choice was ProtobufJS.

Easiest way is to use npm to maintain ProtobufJS, or if you prefer you can use a free CDN

<script src="//cdn.rawgit.com/dcodeIO/protobuf.js/6.X.X/dist/protobuf.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

At the time of writing the stable version is 6.10.2 so the complete inclusion is:

<script src="//cdn.rawgit.com/dcodeIO/protobuf.js/6.10.2/dist/protobuf.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

Bottom line, just find the latest version of protobuf.min.js and include it in your page.

Listen to the WebSocket

The WebSocket implementation is pretty straightforward, and you can find more information here.

The most important change compared to online tutorials I found, is that you need to specify the binaryType of the socket, as shown below

socket.binaryType = 'arraybuffer'
Enter fullscreen mode Exit fullscreen mode

Apart from this change, the implementation is easy:

  1. You create the WebSocket
  2. You listen for the connection to open and send the initial message
  3. You keep listening for incoming messages

Here’s the full code for the WebSocket part

// 1. Socket Init
const socket = new WebSocket('wss://remote-service.com/');
socket.binaryType = 'arraybuffer' // Important!

// 2. Listen to Connection opening
socket.addEventListener("open", function (event) {
    console.log("Connection Opened, sending message");
    socket.send('{"message": "HelloWorld!"}');
};

// Listen to Error Events
socket.addEventListener("error", function(err) {
    console.log("error: ", err);
});

// Listen for Connection closure
socket.addEventListener("close", function() {
    console.log("close");
});

// 3. Most Importantly: Listen for received messages
socket.addEventListener('message', function (event) {
     // Protobuf Implementation here, to manage messages
}
Enter fullscreen mode Exit fullscreen mode

Protobuf to decode the message

If you try to console.log the message received from the last Listener, you will receive a base64 encoded binary array.

This is where Protobuf comes in to decode the message and provide the usable message.

To get started you need to create a .proto file which contains the instructions on how to interpret the binary array you receive. If, like me, you’re implementing this for a remote service, they will provide the .proto file, or you can write one yourself based on their specs. The format is pretty straightforward and it looks like this:

message MyMessage{
  required string title= 1;
  required int32 id = 2;
  optional string text = 3;
}
Enter fullscreen mode Exit fullscreen mode

Once you have the .proto file, just save it and place it in a path that can be reached by the WebServer. In my example I’ve saved it as /js/mymessage.proto.

Now that we have the .proto file ready, we can use it to decode the message that is coming to us from the WebSocket. Expanding the code at point 3 above, we have something like this

socket.addEventListener('message', function (event) {
  // I retrieve the Base64 Encoded string
  msg = event.data
  // I transform such string to the typed array needed
  buffer = Uint8Array.from(atob(msg), c => c.charCodeAt(0))

  // Initiate the Protobuf library by opening the .proto file
  protobuf.load("/js/mymessage.proto", function(err, root) {

    // Retrieve the type of message I want to decode from the .proto file
    var MyMessage = root.lookupType("MyMessage");

    // Finally I can decode my message
    var message = MyMessage.decode(buffer);

    // message now contains an object with the properties specified in the .proto file
    console.log(message)   
  });
}
Enter fullscreen mode Exit fullscreen mode

The whole thing

Here’s the complete script, which should give you a good idea at how to implement a remove WebSocket using Protobuf in Javascript

// 1. Socket Init
const socket = new WebSocket('wss://remote-service.com/');
socket.binaryType = 'arraybuffer' // Important!

// 2. Listen to Connection opening
socket.addEventListener("open", function (event) {
    console.log("Connection Opened, sending message");
    socket.send('{"message": "HelloWorld!"}');
};

// Listen to Error Events
socket.addEventListener("error", function(err) {
    console.log("error: ", err);
});

// Listen for Connection closure
socket.addEventListener("close", function() {
    console.log("close");
});

// 3. Most Importantly: Listen for received messages
socket.addEventListener('message', function (event) {
  // I retrieve the Base64 Encoded string
  msg = event.data
  // I transform such string to the typed array needed
  buffer = Uint8Array.from(atob(msg), c => c.charCodeAt(0))

  // Initiate the Protobuf library by opening the .proto file
  protobuf.load("/js/mymessage.proto", function(err, root) {

    // Retrieve the type of message I want to decode from the .proto file
    var MyMessage = root.lookupType("MyMessage");

    // Finally I can decode my message
    var message = MyMessage.decode(buffer);

    // message now contains an object with the properties specified in the .proto file
    console.log(message)   
  });
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
softmarshmallow profile image
UZU, J

It just makes a lot of sense. Though, I’m curioused why its not adopted by many companies. Wich we don’t know any famous reference using those combinations.

I love grpc but it’s hard to do some load balancing on aws platform, but wesockets are officially supported by aws. For this case it seems like a solution