Article

How to implement WebSocket API JavaScript application with TypeScript

Wasin Waeosri
Developer Advocate Developer Advocate

Overview

Update: September 2021.

Websocket API for Pricing Streaming and Real-Time Service aka Websocket API enables easy integration into a multitude of client technology environments such as scripting and web. This API runs directly on your Refinitiv Real-Time Distribution System and presents data in an open (JSON) readable format. The API supports all Refinitiv data models and can be integrated into multiple client technology standards e.g. JavaScript, Python, R, .Net, etc.

TypeScript programming language is a typed superset of JavaScript that compiles to readable, standards-based JavaScript. The language is designed for application-scale JavaScript by adding optional types, classes, modules, ECMAScript 2015 features, and future proposals to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript is a first-class programming language in Microsoft Visual Studio, Angular web application platform. It also supported by various application frameworks like ReactNodeJS and Express frameworkASP.Net CoreVue.js, and more.

This example shows how to implement the WebSocket API JavaScript web application with TypeScript. The web application source codes are implemented in TypeScript language to connect, consume and display data from the Refinitiv Real-Time Advanced Distribution server via the WebSocket API in the web browsers. All source codes will be compiled to readable JavaScript with Webpack JavaScript module bundler.

*Note: The initial release of this API is for deployed Refinitiv Real-Time Advanced Distribution Server customers only (i.e. to use it you will need an installed version of Refinitiv Real-Time Advanced Distribution Server 3.2.1 and above).

Supported Web Browsers

This example supports the following web browsers (based on the WebSocket and Web Workers browser supported)

  • Chrome
  • Firefox
  • IE11
  • Microsoft Edge (Chrome-based version)

Application files

The project includes complete TypeScript source codes, a simple Express.js web server application file, CSS files, and all required static dependencies. All dynamic dependencies for compiling and building JavaScript source files are defined in the package.json file which can be installed via npm install command.

  • src/ folder: The folder that contains all TypeScript source code files
  • web/ folder: The folder that contains all application web page files
  • web/dist folder: The folder that the compiled JavaScript application file named web_app.js will be generated and deployed
  • web/index.html: The application HTML page
  • web/css/cover.css: The application CSS file
  • web/libs/jquery-3.2.1.min.js: jQuery library file
  • web/bootstrap/cssweb/bootstarp/fonts and web/bootstrap/js folders: The folders for Bootstrap CSS and libraries files
  • package.json: Project's NPM dependencies file
  • tsconfig.json: Project's Typescript compiler options file
  • webpack.config.js: Project's Webpack compiler options file
  • server.js: Project's simple web server application file

Application Code Walkthrough

When building against the WebSocket API interface, you follow the standard HTML5 WebSocket connection initiation and event processing. That is, once you initiate a connection within your application, you define and process the events generated within standard WebSocket callbacks.

Establish a connection

The web_app.ts file implements the standard WebSocket required functions and callbacks to capture WebSocket events. Firstly, the application receives a connect command from the web UI's connect button. The application then establishes a connection with Real-Time Advanced Distribution Server in the connect() function.

    	
            

//web_app.ts

//Initiate WebSocket connection
function connect(url: string): void {
    ws = new WebSocket(url, protocol); 
    ws.onopen = onOpen;
    ws.onmessage = onMessage;
    ws.onerror = onError;
    ws.onclose = onClose;
}

//indicates that the connection is ready to send and receive data
function onOpen(event: any): void {
    console.log("connected");

//$("#btnConnect").html("Connected");

    btnConnect.innerHTML = "Connected";
}

//An event listener to be called when a message is received from the server
function onMessage(event: any): void {
}

//An event listener to be called when an error occurs. This is a simple event named "error".
function onError(event: any): void {
    console.log(JSON.stringify(event.data));
}

//An event listener to be called when the WebSocket connection's readyState changes to CLOSED.
function onClose(event: any): void {
    console.log(JSON.stringify(event.data));
}

Sending Login request message

Next, the application sends the OMM Login request message to Real-Time Advanced Distribution Server in JSON format. In TypeScript, you can create JSON message with JSON object directly (like standard JavaScript) or via TypeScript's Interface and Class approaches. This application uses TypeScript's Interface and Class approach for creating the OMM JSON Login, Item request and Close request messages.

We define the Login interfaces for the OMM Login Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module.

    	
            

//json_msg_interface.ts

//Interface for Login domain JSON message
export interface JSONLogin {
    ID:number;
    Domain:string;
    Key: JSONLoginKey;
}

//Interface for Login domain's Key attribute JSON message
export interface JSONLoginKey{
    Elements: JSONLoginKeyElements;
    Name: string;
}

//Interface for Login domain's Key's Elements attribute JSON message
export interface JSONLoginKeyElements{
    ApplicationId: string;
    Position: string;
}

Then we define the Login classes for the OMM Login Request message in json_msg_classes.ts file. The Login classes implement the Login interfaces based on json_msg_interface.ts file.

    	
            

//json_msg_classes.ts

import { JSONLogin } from "./json_msg_interface";
import { JSONLoginKey } from "./json_msg_interface";
import { JSONLoginKeyElements } from "./json_msg_interface";

export { LoginMsg };

const loginDomain: string = "Login";

class LoginMsg implements JSONLogin {

    ID: number;

    Domain: string = loginDomain;

    Key: JSONLoginKey;

    constructor(

        ID: number,

        Name: string,

        ApplicationId: string,

        Position: string

    ) {     this.ID = ID;

    let loginMsgKey_class = new LoginMsgKey(Name, ApplicationId, Position);

    this.Key = loginMsgKey_class;

    }

}

class LoginKeyElements implements JSONLoginKeyElements {

    ApplicationId: string;

    Position: string;

    constructor(ApplicationId: string, Position: string) {

        this.ApplicationId = ApplicationId;

        this.Position = Position;

    }

}

class LoginMsgKey implements JSONLoginKey {

    Name: string;

    Elements: JSONLoginKeyElements;

    constructor(Name: string, ApplicationId: string, Position: string) {

        this.Name = Name;

        let loginKeyElements_class = new loginKeyElements(ApplicationId, Position);

        this.Elements = loginKeyElements_class;

    }

}

The web_app.ts file initiates the LoginMsg object and then sends it to Real-Time Advanced Distribution Server 3.2 via WebSocket.send() function. This login object will be compiled to the standard Login JSON message in the compiled JavaScript we_app.js file.

    	
            

//web_app.ts

 

import { LoginMsg } from "./json_msg_classes";

const loginID: number = 1;

 

//Create the Login JSON message from LoginMsg class and send it to Real-Time Advanced Distribution Server WebSocket

function sendLogin(username: string): void {

  let login: LoginMsg = new LoginMsg(loginID, username, "777", "127.0.0.1");

  ws.send(JSON.stringify(login));

}

Handling Login Refresh response message

All messages (Refresh, Update, Status, etc) from the Real-Time Advanced Distribution Server will be available via WebSocket.onMessage() callback function. We implement this onMessage() callback function in web_app.ts file to print out incoming messages from Real-Time Advanced Distribution Server in the web browser's inMessagePre <pre> element tag.

    	
            

//web_app.ts

let inMessagePre: any;

//An event listener to be called when a message is received from the server

function onMessage(event: any): void {

    let incomingdata = JSON.parse(event.data.toString());

//Iterate each incoming JSON message

    let data:any = null;

    for (let index = 0; index < incomingdata.length; index++) {

        data = incomingdata[index];

        display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));

    }

}

//display string value in selected element

function display(el:any, msg:string): void{

    el.innerHTML = msg;

}

window.onload = () => {

    ...

    inMessagePre = document.getElementById("inMessagePre");

}

Sending Item request message

Next, we implement the application source code to send the OMM Market Price request message to the Real-Time Advanced Distribution Server when users click the Subscribe button in the web UI.

We define the Item Request interfaces for the OMM Market Price Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module. We set the Real-Time Advanced Distribution Server Service information as optional information in the JSONItemRequestKey interface because it is an optional parameter in the OMM Request message.

    	
            

//json_msg_interface.ts

//Interface for Market Price domain item request JSON message

export interface JSONItemRequestMsg {

    ID: number;

    Key: JSONItemRequestKey;

}

//Interface for Market Price domain item request's key attribute JSON message, service name is an optional

export interface JSONItemRequestKey {

    Name: string;

    Service?: string;

}

Next we define the Item Request classes for the OMM Market Price request message in json_msg_classes.ts file. The Item Request classes implement the Item Request interfaces based on json_msg_interface.ts file.

    	
            

//json_msg_interface.ts

import { JSONItemRequestMsg } from "./json_msg_interface";

import { JSONItemRequestKey } from "./json_msg_interface";

export { ItemRequestMsg };

class ItemRequestMsg implements JSONItemRequestMsg {

    ID: number;

    Key: JSONItemRequestKey;

    constructor(

        ID: number,

        Name: string,

        Service: string

    ) {

    this.ID = ID;

    let itemrequestKey_class = new ItemRequestMsgKey(Name, Service);

    this.Key = itemrequestKey_class;

    }

}

class ItemRequestMsgKey implements JSONItemRequestKey {

    Name: string;

    Service?: string;

    constructor(

        Name: string,

        Service?: string

    ) {

            this.Name = Name;

            if (Service !==''){

            this.Service = Service;

        }

    }

}

The web_app.ts file initiates the ItemRequestMsg object based on user input from the web UI and sends it to Real-Time Advanced Distribution Server via WebSocket.send() function. This item request message object will be compiled to a standard Item Request JSON message in the compiled JavaScript we_app.js file.

    	
            

//web_app.ts

 

import { ItemRequestMsg } from "./json_msg_classes";

let itemID: number = 0;

let itemname:string = "";

 

//Create the Item Request JSON message from ItemRequestMsg class and send it to Real-Time Advanced Distribution Server WebSocket

function sendItemrequest(service: string, item_name: string): void {

  //set Item ID value

  if (itemID === 0) {

    itemID = loginID + 1;

  } else {

    itemID += 1;

  }

 

  let itemrequest: ItemRequestMsg = new ItemRequestMsg(

    itemID,

    item_name,

    service

  );

 

  ws.send(JSON.stringify(itemrequest));

  display(outMessagePre,JSON.stringify(itemrequest));

 

}

 

window.onload = () => {

 

  btnSubscribe.addEventListener("click", function() {

    let txtServiceName:any = document.getElementById("txtServiceName");

    let txtItemName:any = document.getElementById("txtItemName");

    itemname = txtItemName.value;

    sendItemrequest(txtServiceName.value, itemname);

  });

};

Handling incoming Item response message

All messages (Refresh, Update, Status, etc) for all Domains from the Real-Time Advanced Distribution Server will be available in WebSocket.onMessage() callback function. We will modify the onMessage() callback function in web_app.ts file to support incoming Market Price data from Real-Time Advanced Distribution Server.

The onMessage() callback function verifies if an incoming message is an incoming Market Price Refresh Response message or not by checking the data type and data.Key.Name property. If it is the Market Price Refresh message, it calls the pushIDstoDropDownMenu() function to add this item name and subscription id to the web UI list box. These item names and subscription id information will be used for unsubscribing items.

The onMessage() callback function also prints out incoming Market Price messages from Real-Time Advanced Distribution Server in the web browser's inMessagePre <pre> element tag.

    	
            

//web_app.ts
let inMessagePre: any;

let itemname:string = "";

//An event listener to be called when a message is received from the server

function onMessage(event: any): void {

    let incomingdata = JSON.parse(event.data.toString());

//Iterate each JSON message and send it to market_price_app.js

    let data:any = null;

    for (let index = 0; index < incomingdata.length; index++) {

        data = incomingdata[index];

        if(data.Type === 'Refresh' && data.Key.Name === itemname){

//Push subscription item name and ID to selection box

            pushIDstoDropDownMenu(data.Key.Name, data.ID);

        }

    display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));

    }

}

//display string value in selected element

function display(el:any, msg:string): void{

    el.innerHTML = msg;

}

window.onload = () => {

    ...

    inMessagePre = document.getElementById("inMessagePre");

}

function pushIDstoDropDownMenu(itemname:string, id:number):void {

    let cb:any = document.getElementById("listenerCombo");

    let opt:any = document.createElement("option");

    opt.value = id;

    opt.text = `${itemname} ID ${id}`;

    cb.options.add(opt);

}

Unsubscribe Market Price Item

Next, we implement the application source code to send the OMM Market Price close request message to Real-Time Advanced Distribution Server (version 3.2.1 and above). We define the Close Request interfaces for the Close Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module. We set the Domain information as an optional In the JSONClose interface because it is an optional parameter in the OMM Close message (Market Price by defaulted).

    	
            

//json_msg_interface.ts

//Interface for close JSON message

export interface JSONClose {

    Domain?: string;

    ID: number;

    Type: string;

}

Next we define the Close Message classe in json_msg_classes.ts file. The Close Message class implements the JSONClose interface based on json_msg_interface.ts file.

    	
            

//json_msg_interface.ts

import { JSONClose } from "./json_msg_interface";

export {CloseMsg};

class CloseMsg implements JSONClose{

    Domain?: string;

    ID: number;

    Type: string;

    constructor(ID: number, Domain?: string) {

        this.ID = ID;

        this.Domain = Domain;

        this.Type = "Close";

    }

}

When unsubscribing item, users must choose the item name and subscription id that users want to unsubscribe from the web UI list box, then click the Unsubscribe button. We implement this UI interaction in web_app.ts file.

    	
            

//web_app.ts

window.onload = () => {

    btnUnSubscribe.addEventLisener("click", function() {

//get Selected ID to unsubscribe let cb:any =     document.getElementById("listenerCombo");

    if(cb.selectedIndex === -1){

//If user does not select any ID, alert user

        window.alert("Select ID first"); return;

    }

//get unsubscribe ID from HTML select element

    let unsubID:number = parseInt(cb.options[cb.selectedIndex].value);

//Unsubscribe sendItemCloserequest(unsubID);

//remove unsubscribe ID from HTML select element

    cb.removeChild(cb.options[cb.selectedIndex]);

    });

};

The web_app.ts file initiates the closeitemrequestMsg object, receives user input from the web UI, and then sends it to Real-Time Advanced Distribution Server via WebSocket.send() standard function. This item close request message object will be compiled to a standard Item Close Request JSON message in the compiled JavaScript we_app.js file.

    	
            

//web_app.ts

import { CloseMsg } from "./json_msg_classes";

//Create the Item Close Request JSON message from ItemCloseRequestMsg class and send it to ADS WebSocket

function sendItemCloserequest(unsubID:number): void {

//let closeitemrequestMsg: ItemCloseRequestMsg = new ItemCloseRequestMsg(itemID);

    let closeitemrequestMsg: CloseMsg = new CloseMsg(unsubID);

    ws.send(JSON.stringify(closeitemrequestMsg));

    display(outMessagePre,JSON.stringify(closeitemrequestMsg));

}

Handling Ping Pong messages

The last step is handling the Ping and Pong messages. The Ping and Pong messages are the handshake message in JSON format ({ "Type": "Ping" } and { "Type": "Pong" }) between the Real-Time Advanced Distribution Server WebSocket server and client for monitoring connection health. The Real-Time Advanced Distribution Server periodically sends Ping messages to applications and applications must be prepared to send Pong messages as a response to any Ping message they receive.

The application will receive the Ping message via WebSocket.onMessage() callback function. We will modify this function to detect an incoming Ping message, then sends the Pong message back to Real-Time Advanced Distribution Server.

    	
            

//web_app.ts

 

//An event listener to be called when a message is received from the server

 

function onMessage(event: any): void {

 

  let incomingdata = JSON.parse(event.data.toString());

 

  //Iterate each JSON message and send it to market_price_app.js

  let data:any = null;

  for (let index = 0; index < incomingdata.length; index++) {

    data = incomingdata[index];

 

    if(data.Type === 'Refresh' && data.Key.Name === itemname){

      //Push subscription item name and ID to selection box

      pushIDstoDropDownMenu(data.Key.Name, data.ID);

    }

    display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));

    //If incoming message is PING (server ping)

    if (incomingdata[index].Type === "Ping") {

      sendPong();

    }

  }

}

 

//Create the client PONG message  and send it to Real-Time Advanced Distribution Server WebSocket

function sendPong(): void {

  let pong: any = { Type: "Pong" };

  ws.send(JSON.stringify(pong));

  display(outMessagePre,JSON.stringify(pong));

}

Application Setup

The application source code is available at GitHub. You can get it via the following git command

    	
            
$>git clonegit@github.com:Refinitiv-API-Samples/Example.EWA.TypeScript.WebApplication.git

This example requires the following dependencies software and runtime

  1. Node.js runtime - version 8.9.3 or higher.
  2. npm package manager (included in Node.js)
  3. TypeScript compiler (will be installed via npm install command)
  4. Express.js web framework (will be installed via npm install command)

This example also uses the following 3rd party libraries for UI presentation only.

  1. jQuery 3.2.1 JavaScript library
  2. Bootstrap 3.3.7 CSS library

please check the README.md in the source code project for more detail.

How to run this example

  1. Get the project via the above git command
  2. Run $> npm install command in the command prompt to install all the required dependencies to run the sample in a subdirectory called node_modules/.

3.    If the machine is behind a proxy server, you need to configure Node.js uses proxy instead of a direct HTTP connection via the following command in command prompt: 

    	
            
set https_proxy=http://<proxy.server>:<port>

4.    Run $> npx webpack command in the command prompt to build and compile all TypeScript files in src folder into JavaScript source file (/web/dist/ folder)

5.    Run $> node server.js command in the command prompt to start the webserver at HTTP port 8080

6.    Open a web browser (IE11, Chrome/Microsoft Edge (Chrome-based version), and Firefox), then navigate to index.html of the webserver at http://localhost:8080/index.html