Community Post

Syncs: A JavaScript Library for Real-Time Web Applications

Mostafa Alinaghi-pour

Real-Time Web

Customer expectations are rising faster than web technologies growth. Customers expect an online application to be fast, smooth and easy to use. In most of web application users prefer to use online application as a desktop application which works fast and real-time. The Real-Time Web enables users to have information and result as soon as they are available, rather than requiring that they or their software check a source periodically for updates. Most of web developers use polling techniques to simulate real-time environment in their application but it will cause huge network call and process overhead. Syncs works as a middleware to enables real-time, bi-directional communication between web clients and web servers. Syncs provides four abstraction layer over its real-time functionality for developers. There will be lots of use case for real-time web application in future because most of web services are trying to offer our traditional services in real word in modernized and digitated form and we interact without word and society in real-time form.

Setting Up

Syncs Is a powerful and simple to use library. It uses JavaScript language for both client and server scripting, so it can be easy to initiate. Also it is easy to initialize. Syncs is developed with typescript language. Sample projects are avilable here.

Server setup

Add Syncs npm package to your dependencies and run npm install command or

npm install syncs --save

Syncs instance works with native nodejs http server.

import * as http from "http";
import syncs from "syncs";
let server=http.createServer()
let io=syncs(server);
server.listen(8080);

Also you are able to use light web frameworks like express.

import * as http from "http";
import * as express from "express";
import syncs from "syncs";

let app=express();
let server=http.createServer(app);
let io=new syncs(server);

server.listen(8080);

Client Initialization

Syncs clients are developed to run on both Browser, NodeJs and Java :

Now we just use browser script. There is two way to setup the Browser Client Script. Developers can download javascripts file from this link and add the syncs.js file to scripts directory.

Or on server side it's possible to access client script from Syncs instance:

        app.get('/syncs.js',(req,res)=>{
            res.send(io.clientScript);
        });

After serving client script, developers should include it in html page and create an instance of Syncs class.

    <!doctype html>
    <html >
    <head>
    <meta charset="UTF-8">
         <script src="syncs.js"></script>        
    </head>
        <body>

        </body>
        <script>
            let io=new Syncs();
        </script>
    </html>

Client Script constructor has config parameter:

  • path:string: WebSocket url begins with ws:// protocol. default value is ws://domain/realtime which domain name will sets automatically.
  • autoConnect:boolean: If autoConnect is false then the Syncs instance will not connect to server on creation. To connect manuly to server developers should call io.connect() method. default value is true.
  • autoReconnect:boolean: This config makes the connection presistent on connection drop. default value is true.
  • reconnectDelay: number: time to wait befor each reconnecting try. default value is 1000.
  • debug:bolean: This parameter enables debug mode on client side. default value is false.

Syncs Real-Time solutions

Syncs have four abstraction layer over its real-timing functionality that can boost development speed and give full access over persistent connection to developers.

onMessage abstraction layer

This type of real-time development is primary type between other solutions and uses fundamental functionality of WebSocket. If data and functionality around that in web is simple, onMessage as a simple messaging solution may fit developers need. With Syncs it’s possible to access WebSocket functionality in low level mode. In this mode developer should handle all data transfer controls. Developers can send messages using send method of SyncsClient instance on server side to send JSON message to the client. on client side, by using onMessage method of Syncs instance developers can recive all incoming messages.

//server side
io.onConnection(client=>{
    client.send({text:"welcome"});
})
//client side
io.onMessage(message=>{
    alert(message.text);
})

It's possible to send message from client to server.

//client side
function setName(newName){
    io.send({name:newName});
}
io.onMessage((message,client)=>{
    client.data.name=message.name;
})

On server side it's possible to send messages to group of clients

//server side
function sendServerTime(){
    io.group('authenticated').send({time:new Date().toTimeString()})
}

Developers should add extra properties to distinguish between messages.

Publish and Subscribe Abstraction Layer

Using this solution developers normally subscribe to data using a string identifier. This is normally called a Channel, Topic or Subject. Both server and client can publish or subscribe to event. publish method is accessible from SyncsServer, SyncsGroup and SyncsClient instances on server side.

// server side
//sends time too all clients
function sendTime(){
    io.publish('time-report',{time:new Date})
}

//add client to group and report client entrance to group
function registerInGroup(groupName:string,client:SyncsClient){
    io.group(groupName).add(client).except(client).publish('new-member',{name:client.data.name});
}

//publish direct message to single client
function directMessage(client:SyncsClient,message:string,from:string){
    client.publish('direct-message',{from:from,message:message});
}

function waitForMessage(){
    io.subscribe('public-message',(data,client)=>{
        io.publish('public-message',{message:data.message,from:client.data.name})
    })
}
//cliet side
io.subscribe('time-report',data=>{
    console.log(data.time);
})
io.subscribe('new-member',data=>{
    //update members list
})
io.subscribe('direct-message',data=>{
    //show direct message to user
})

function sendPublicMessage(message:string){
    io.publish('public-message',{message:message});
}

It's possible to disable subscription to event using unSubscribe method.

Shared Data Abstraction Layer

Syncs provides Shared Data functionality in form of variable sharing. Shared variables can be accessible in tree level: Global Level, Group Level and Client Level. Global Level and Group Level shared objects are readonly by client. Client Level shared object are write-able by client but server can make readonly client level shared object.

//server side
// reporting online users to all clients
let info=io.shared('info');
info.onlineUsers=0;
io.onConnection(client=>{
    info.onlineUsers++;
});
io.onClientClose(client=>{
    info.onlineUsers--;
})
//client side
let info=io.shared('info');

function logOnlineUsers(){
    console.log(info.onlineUsers);
}

It's possible to get shared variable in Group Level

//server side
function setGroupTopic(groupName:string,topic:string){
    io.group(groupName).shared('settings').topic=topic;
}

Client Level shared object by default is write-able by client.

    // server side
    function setName(client,name){
        client.shared('profile').name=name;
    }
    //client side
    function setName(name){
        io.shared('profile').name=name;
    }

Developers can create read only variables by passing second parameter to share method.

    //server side
    client.shared('session',true).id=getSessionId();

It's possible to add listener to check shared variable change. shared variable object returned by shared method can bee called as a function to add listener. Developers should pass a callback function to handle change event.

// client side
let info=io.shared('info');

info((values,by)=>{
   //handle changes
});

The callback function has two argument.

  • values:object: an object that contains names of changed properties and new values.
  • by:string a string variable with two value ( 'server' and 'client') which shows who changed these properties.

Remote Method Invocation (RMI) Abstraction Layer

With help of RMI developers can call and pass argument to remote function and make it easy to develop robust and web developed application. RMI may abstract things away too much and developers might forget that they are making calls over the wire.

Before calling remote method developer should declare the function on client or server script.

functions object in io is the place to declare functions.

//clint side
io.functions.showMessage=function(message) {
  alert(message);
}

The caller side can access remote method using remote object.

//server side
cliet.remote.showMessage('welcome...');

The remote side can return a result which is accessible using Promise object returned by caller side.

//client side
io.functions.getUserVote=function(message:string,options:string[]){
    let userVote=askUser(message,options);
    return userVote;
}
//server side
cliet.remote.getUserVote(
    "Which programming language is better?",
    ['C#','Java','Javascript','Python']).then(result=>{
        // handle result ...
    })

Remote side can return another Promise object if the result is not accessible yet.

Conclusion

Real-Time Web will be upcoming trend in web field. As HTTP 1.1 is request response base developers need to consider existing real-timing solutions. Do developers really want to write code to deal with connection fallback/upgrade strategy? Surely it's better to use a solution that has that in place. Each developer team should have some consideration while they are developing real-time application.

  • Horizontal scalability by using clustering functionality of real-time library.
  • Vertical scalability (and performance in general).
  • Security of data including data entitlement.
  • Guaranteed delivery of data in presence of failures.
  • Real-Time library flexibility.
  • Level of abstraction over real-timing processes. Syncs seeks to satisfy most of developer needs for producing a real-time application by providing direct access to WebSocket interface or by abstracting on WebSocket native tools.
    Real-Time functionality is becoming more important and beneficial. Other existing frameworks are adding functionalities that Syncs provides and maybe it won't be long until real-time will be a standard part of most application frameworks and applications.