DEV Community

maarteNNNN
maarteNNNN

Posted on

SocketCluster. The most underrated framework. Part 2: A simple example

GitHub logo maarteNNNN / sc-underrated-framework-part2

A repo that represents the code from an article I wrote on dev.to

Part 2: A simple example

Recap

In Part 1 we gave an introduction to what SocketCluster is, does and which projects/companies use it.

Setting up the SocketCluster project.

NOTE
In this article I will focus solely on SocketCluster and not implement Vue yet. We will do some vanilla client js in the index.html file served by SocketCluster.

We'll need the socketcluster-cli to be able to scaffold a project. Install it by executing npm i -g socketcluster. Initiating a project can be done with socketcluster create <project-name>. Let's open our code editor and take a look at what we have. The project consists of two directories kubernetes and public and some files. kubernetes is where all the config files to deploy as a kubernetes service. We will discuss this in a later article. public is a directory served by Express and upon firing up SocketCluster it will be accessible by navigating localhost:8000. Let's fire up SocketCluster and see what we can do by running npm run start. The default port is 8000.

Let's take a look where the magic happens. The server.js file. The server.js consists of some sections, the beginning are mostly constants to environment variables. Then it creates two servers. One is the httpServer and the other is the agServer. The agServer handles the sockets. The httpServer is a HTTP server wrapped in eetase. Which basically adds a request to a queue to be able to run for await (let ... of asyncQueueIterable) { ...logic... } loop on the requests. Then comes the Express part. It defines which directory to serve and provides a /health-check route to see if the server is operational.

// HTTP request handling loop.
(async () => {
  for await (let requestData of httpServer.listener('request')) {
    expressApp.apply(null, requestData);
  }
})();
Enter fullscreen mode Exit fullscreen mode

This piece of code calls every request asynchronously to the expressApp variable and passes the requestData as the first argument, basically handling the request. This is where the eetase comes in handy to queue up requests and carefully execute them one by one.

Let's create an endpoint

We will create an endpoint in the server.js first. This is the entrypoint for every socket connection. Let's add some code:

for await (let { socket } of agServer.listener('connection')) {
...
  for await (let request of socket.procedure('test')) {
    console.log(request.data);
    request.end({ message: 'Data received, thanks client!' });
  }
...
}
...
Enter fullscreen mode Exit fullscreen mode

This will handle any request on the test procedure. Log the given data to the console and respond with the message Data received, thanks client!.

Now let's add some code to actually test this route on the client. Let's use the index.html in the public folder so we can do some basic testing. Add a button below the iframe block in the HTML:

...
<!-- IFRAME BLOCK -->
<button onclick="send()">Test</button>
...
Enter fullscreen mode Exit fullscreen mode

And add some JavaScript logic below let socket = socketClusterClient.create();

const send = async () => {
  console.log('sending to the server...');
  const response = await socket.invoke('test', {
    message: 'This is our first message from the client to the server',
  });
  console.log(response);
};
Enter fullscreen mode Exit fullscreen mode

Restart the server and go to localhost:8000, open your developer console and hit the button Test. Upon clicking you should receive the Data received, thanks client! in the browser and when going to the terminal it should show This is our first message from the client to the server.

Now you succesfully created an endpoint from the client to the server. Now you can do some cool things but let me show you that you can do it the other way around as well. Unlike REST, SocketCluster lets you handle messages both from the server to the client as well as from the client to the server. Let's make a quick example sending an interval to the client.

On the server we will add the interval of 10 seconds:

...
const send = async () => {
  console.log('sending to the server...')
  const response = await socket.invoke(
    'test',
    { message: 'This is our first message from the client to the server' },
  );
  console.log(response)
}

setInterval(async () => {
  console.log('sending data to client...');
  const data = await socket.invoke('from-server', {
    message: 'This is sent from the server to the client',
  });

  console.log(data);
}, 10000);
...
Enter fullscreen mode Exit fullscreen mode

And on the client we will listen to the procedure:

...
const send = async () => {
  console.log('sending to the server...')
  const response = await socket.invoke(
    'test',
    'This is our first message from the client to the server',
  );
  console.log(response)
}

(async () => {
  for await (let request of socket.procedure('from-server')) {
    console.log(request.data);
    // return this message to the server, it could just be request.end() to terminate it
    request.end({ message: 'Thanks server, message received' });
  }
})();
...
Enter fullscreen mode Exit fullscreen mode

Restart the server and refresh the browser window. You should receive the message This is sent from the server to the client every 10 seconds.

Top comments (0)