The Router

The Finsemble Router sends and receives messages between windows, enabling communication between all Finsemble components and services. The Router Client API specifies the Router's interface, but a higher level introduction is given here, describing the three types of messaging models supported.

The diagram below illustrates the Router as the center point of all Finsemble communication. Again, all communication between components and services takes place through the Router.

Router Overview Diagram

There are three distinct models within the Router for exchanging event messages:

  1. Listen/Transmit: One-way communication on a channel between one or more transmitters to one or more listeners.
  2. Query/Response: Two-way communication between one or more "clients" querying a channel "responder."
  3. PubSub: A subscribe-publish-notify exchange where the subscribers (which receive state notifications) have no couplings to the publishers of state, which is maintained within a PubSub responder that bridges communication.

Note Note: The messaging models are separated, which primarily means a message channel (or topic) used in one model is distinct from the same channel name used in another model—there is no cross talk. Also, because these message exchanges are usually between different windows, they are asynchronous.

First supported model: Listen/Transmit

The diagram below shows the basic usage of Listen/Transmit, where one transmitter (or more) can communicate with one or more listeners over a specific virtual channel. More specifically, a transmit event is duplicated by the Router Service and sent to each of the corresponding listeners, whose callback is then invoked with the transmit data.

Channels do not have to be predefined, but a transmit event won't be received without at least one corresponding listener in place—there is no message queuing. It is up to a service or component to document the specific channel names it uses when building new functionality; although, channel names can also be dynamically defined and pass to clients through other established channels—it all depends on the functionality being provided. Also, once a listener is added for a channel, it will stay in place, receiving incoming transmits to the channel until the listener is removed.

If a transmit event occurs to a channel without any listeners, a diagnostic message will be logged (when debug is enabled) by the Router Service, but no run-time error is passed back to the one-way transmitter—it is the responsibility of services and components to properly set up listeners either during initialization or when a specific service is invoked.

Data Manager Diagram

Listen/Transmit example

Clients B and C in the above diagram can use something like the following code snippet to create a listener for ChannelA. With this code, events will be passed to the callback function for all incoming transmits to ChannelA. As long as the error parameter is null, the response parameter will contain the incoming transmit event.

FSBL.Clients.RouterClient.addListener("ChannelA", function(error,response) {
	if (error) {
		console.log("ChannelA Error: ' + JSON.stringify(error));
	} else {
		console.log("ChannelA Response: ' + JSON.stringify(response));

Client A in the following snippet transmits to all ChannelA listeners, triggering events in Clients B and C in this instance. Any data consistent with JSON can be sent in the transmit event.

FSBL.Clients.RouterClient.transmit("ChannelA", {
	dataFieldOneA: 1,
	dataFieldTwoA: { subfieldA: 2 },

In summary, any channel can have multiple transmitters and listeners. If there are no listeners for a channel, the corresponding transmit will be discarded (with a warning sent to the Router Service log). The same Router Client can transmit and listen on the same channel. The same Router Client can also add multiple listeners for the same channel. Often channel listeners are defined during initialization for components and service, but they can also be dynamically defined (with their names distributed to other clients over other channels using one of the messaging models).

Second supported model: Query/Response

The diagram below shows the basic usage of the two-way Query/Response model, in which a query is routed to a corresponding responder for a specific virtual channel and a corresponding response is returned. More specifically: 1) a query is routed to the corresponding responder; 2) the responder's callback is invoked; 3) a query response is sent from within the responder callback; and 4) the query response is routed back to the query client's callback. The two main distinctions are that only one responder can exist for a given virtual channel, and each time a responder receives an incoming query it is required to send back a corresponding response. As with listeners, once a responder is added it stays in place receiving and responding to incoming queries until it is removed.

If a responder is added to a channel that already has a responder, an error is returned. If a query is sent to a channel without a responder, an error is returned.

Data Manager Diagram

Query/Response Example

Client B in the diagram above can use something like the following snippet to create ResponderA to catch corresponding incoming queries and respond to them. Note the queryMessage passed into the addResponder's callback function contains the function required to send back the corresponding response—this sendQueryResponse() function should always be called within the responder's callback function.

FSBL.Clients.RouterClient.addResponder("ResponderA", function(error, queryMessage) {
	if (!error) {
		console.log("Responder A Query: ' + JSON.stringify(queryMessage));
		queryMessage.sendQueryResponse(null, { field1: "query response data", field2: } );

For the other half, Client A in the following snippet sends a query to ResponderA and processes the corresponding query response when it is received. Note that optional data can be included in the query request.

FSBL.Clients.RouterClient.query("ResponderA", { count: 1 }, function (error, response) {
	if (!error) {
		console.log("Responder A Query Response Response: " + JSON.stringify(response));

Third supported model: PubSub

The diagrams below illustrate the usage and message flow of the PubSub model. PubSub has three primary players:

  1. Subscribers which receive state (i.e., data) updates as published to a specific topic—a topic is essentially a virtual channel. Each subscriber will always receive an initial notification with the current state for a topic. Then, each time a new state is published to a topic, all the subscribers receive notifications with the new state data.
  2. Publishers of state data for a specific topic. Again, each publish event will result in a corresponding notification being sent to all of the subscribers.
  3. A topic's Responder must always be defined, but comes with sufficient default built-in functionality to handle most cases. The responder provides a bridge between subscribers and publishers (i.e., subscribers have no knowledge of publishers and visa versa).

What's different for the Finsemble PubSub model is that the responder isn't automatically "built-in" for all topics like in some PubSub implementations. By explicitly requiring that a responder be added for each topic, better diagnostics can be provided. Also, when a responder is added it allows the responder client to provide optional callbacks to supplement the default behavior—default behavior includes delivering to each new subscriber an initial notification with the current state and then a new notification and state for each subsequent publish event.

Three optional callbacks can be provided when a PubSub responder is added.

  1. A subscribe callback for invocation each time the responder receives a new subscription request. The callback can either "accept" or "reject" the subscription. The default behavior is to accept all subscriptions.
  2. A publish callback for invocation each time the responder receives a new publish request. The callback can either "accept" the publish request resulting in sent notifications, "reject" it with an error causing the publish to be discarded, or "modify/augment" the publish state data before accepting the publish request (with notifications then sent). The default behavior is to accept the new state data and send notifications to all subscribers.
  3. A unsubscribe callback for invocation each time the responder receives a new unsubscribe request. In this case, the default behavior cannot be modified by the callback, but it provides a hook to take additional action before the unsubscribe is completed.

Note Note: If the optional subscribe or publish callbacks are defined, then it is up to PubSub responder client to maintain the internal state for the given topic so it can be returned in the callback responses.

Data Manager Diagram

PubSub Example

Client E in the diagram above can use the command below to define a PubSub responder for TopicA. This step must be done before any subscribe or publish callbacks. Only the topic and starting state are defined in this snippet (i.e., no optional callbacks are defined) so the default functionality will be provided for all subscribe and publish callbacks to TopicA.

FSBL.Clients.RouterClient.addPubSubResponder("topicA", { State: "start" });

Client A and Client B both publish to TopicA using the following snippets, each defining new states for TopicA. Since publishers and subscribers are decoupled, the commands can be issued before or after the subscribe callbacks; the only difference is what the current state will be when the subscribe callback occurs.

Note: The state data is completely free-form—it is up to PubSub responder for TopicA to define the formats of state data it expects.

FSBL.Clients.RouterClient.publish("topicA", { "NOTIFICATION-STATE": "One" });

FSBL.Clients.RouterClient.publish("topicA", { "NOTIFICATION-STATE": "Two" });

Clients C and D can both use something like the following snippet to subscribe to TopicA. An initial state is always returned in an underlying notification message causing the defined subscribe callback to be invoked. Subsequent publishes sent to the PubSub responder will result in the same subscribe callback being invoked as each corresponding notifications are received.

FSBL.Clients.RouterClient.subscribe("topicA", function (error, notify) {
	if (error) {
		console.log("Topic A Subscribe Error:" + JSON.stringify(error));
	} else {
		console.log("Topic A Notification:" + JSON.stringify(notify));

As outlined earlier, the PubSub responder can optionally catch the incoming events for subscribe, publish, or unsubscribe events using something like the following snippet—the callbacks are defined when the PubSub responder is added. Notice that each callback message includes a callback-response function (e.g., sendNotifyToSubscriber, sendNotifyToAllSubscribers, or removeSubscriber). If a callback-response has the error parameter set, then it essentially rejects the incoming request.

As previously noted, when callbacks for subscribe or publish are defined, it is up to the corresponding client code to maintain the topic's state and return it in the callback (or just pass it from the incoming message in the publish case).

function subscribeCallback(error, subscribe) {
	if (!error) {
		subscribe.sendNotifyToSubscriber(null, { "NOTIFICATION-STATE": "One" });
function publishCallback(error, publish) {
	if (!error) {
function unsubscribeCallback(error, unsubscribe) {
	if (!error) {

	{ State: "start" },

If any of the optional callbacks" (subscribeCallback, publishCallback, or unsubscribeCallback) are defined in addPubSubScriber, then it is up to PubSub responder client to maintain the internal state for the given topic so it can be returned in the callback responses -- for this case the responder automatically resides within client as shown in the diagram below.

The diagram below illustrates PubSub behavior for the same example above but without default functionality, where all PubSub behavior is handled on the client side to change default functionality (as opposed to within the Router Service where default functionaity is provided). This approach provides extra control, but the PubSub communications requites double the message hops (from originator to RouterService and from Router Server to the client where the pubsub responder is defined) instead of the one hop shown (as shown in the first diagram when default behavior is used).

Data Manager Diagram

PubSub with a RegEx topic

Occasionally, you may want to define a range of PubSub topics all at once, typically with built-in functionality for each, as shown in the following PubSub responder:

RouterClient.addPubSubResponder(/topicB.*/, { NOTIFICATION: "start" }); // RegEx Topic for new PubSubResponder

This essentially defines any PubSub responder starting with the topic substring topicB, enabling any of the following subscriptions or publish events along with the resulting default functionality for each individual topic.

FSBL.Clients.RouterClient.subscribe("topicB12345", notifyCallback);
FSBL.Clients.RouterClient.subscribe("topicB123", notifyCallback);
FSBL.Clients.RouterClient.subscribe("topicB2", notifyCallback);

FSBL.Clients.RouterClient.publish("topicB12345", { NOTIFICATION: "one" });
FSBL.Clients.RouterClient.publish("topicB123", { NOTIFICATION: "one" });
FSBL.Clients.RouterClient.publish("topicB2", { NOTIFICATION: "one" });

The underlying transport of event messages

For extensibility, the Router design is decoupled from the underlying point-to-point transports between Router Clients and the central Router Service. Currently, we use three: HTML5 SharedWorker, Inter-application Communication, and Electron's Inter-process Communication.

  • SharedWorker transport is used by default for all same-domain HTML components.
  • Inter-process Communication (IPC) is used for cross-domain HTML components.
  • Inter-application Communication (IAC) is used for communication between HTML and native components. IAC uses WebSockets as its communications protocol. IAC can be configured to use custom certificates when connecting to a secure address. See below for more information.

Finsemble also provides support for external WebSockets for plugging in custom/proprietary transports.

Custom WebSocket Certificates

If your internal security procedures block Finsemble's transport certificates, you can use custom certificates instead. Custom WebSocket certificates can be configured for development and production use over the Inter-application Communication (IAC) transport.

Once configured, these certificates are used automatically when running locally. They can also be bundled in an installer.

Certificate locations are defined in ../configs/other/server-environment-startup.json.

  1. Under the development or production section (depending on how you intend to run Finsemble) uncomment the following two lines:
"//socketCertificateKey": "example.key",
"//socketCertificateCert": "example.crt"
  1. Replace example.key and example.crt with paths to the certificate location. These paths must be relative to the location of your Finsemble directory. Note that both lines must contain valid file paths for the certificate files to be used.

check   The Router supports the Listen/Transmit, Query/Response, and PubSub methods of communication.

Further Reading

Advanced information about the Router can be found in the Router Client documentation.

The Finsemble Router is an important part of the Finsemble Library, as discussed in Lifecycle Events.