API Tutorial - Channels

Overview

In order for data to be transmitted, the provider will need to create a new channel. This channel will create a pathway between the ChannelClient and ChannelProvider for messaging. Messages can then be dispatched from the channelProvider to any channelClient or from any channelClient to the ChannelProvider.

To setup a channel as a channelProvider

  • call Channel.create with a unique channel name. A map of client connections is updated automatically on client connection and disconnection
  • To complete channel client setup, a connection is made to a channel as a channelClient by calling Channel.connect with a given channel name

ChannelProvider Setup

A channel can be created with a unique channel name by calling Channel.create. If successful, the create method returns a promise that resolves to an instance of the channelProvider bus. The caller then becomes the “channel provider” and can use the channelProvider bus to register actions and middleware. The caller can also set an onConnection and/or onDisconnection listener that will execute on any new channel connection/disconnection attempt from a channel client. To reject a connection, simply throw an error in the onConnection listener. The default behavior is to accept all new connections. A map of client connections is updated automatically on client connection and disconnection and saved in the [read-only] connections property on the channelProvider bus. The channel will exist until the provider destroys it or disconnects by closing or destroying the context (navigating or reloading).

Example

async function makeProvider() {
   // entity creates a channel and becomes the channelProvider
   const providerBus = await fin.InterApplicationBus.Channel.create('channelName');
  
   providerBus.onConnection((identity, payload) => {
       // can reject a connection here by throwing an error
       console.log('Client connection request identity: ', JSON.stringify(identity));
       console.log('Client connection request payload: ', JSON.stringify(payload));
   });

   providerBus.register('topic', (payload, identity) => {
       // register a callback for a 'topic' to which clients can dispatch an action
       console.log('Action dispatched by client: ', JSON.stringify(identity));
       console.log('Payload sent in dispatch: ', JSON.stringify(payload));
   });
}

ChannelClient Setup

A connection can be made to a channel as a channelClient by calling Channel.connect with a given channel name. The connection request will be routed to the channelProvider if/when the channel is created. If the connection request is sent prior to creation, the promise will not resolve or reject until the channel is created by a channelProvider (whether or not to wait for creation is configurable in the connectOptions). The connect call returns a promise that will resolve with a channelClient bus if accepted by the channelProvider, or reject if the channelProvider throws an error to reject the connection. This bus can communicate with the Provider, but not to other clients on the channel. Using the bus, the channelClient can register actions and middleware. Channel lifecycle can also be handled with an onDisconnection listener.

Example

async function makeClient(channelName) {
   // A payload can be sent along with channel connection requests to help with authentication
   const connectPayload = { payload: 'token' };

   // If the channel has been created this request will be sent to the provider.  If not, the
   // promise will not be resolved or rejected until the channel has been created. 
   const clientBus = await fin.InterApplicationBus.Channel.connect('channelName', connectPayload);

   clientBus.onDisconnection(channelInfo => {
       // handle the channel lifecycle here - we can connect again which will return a promise
       // that will resolve if/when the channel is re-created.
       makeClient(channelInfo.channelName);
   })

   clientBus.register('topic', (payload, identity) => {
       // register a callback for a topic to which the channel provider can dispatch an action
       console.log('Action dispatched by provider: ', JSON.stringify(identity));
       console.log('Payload sent in dispatch: ', JSON.stringify(payload));
       return {
           echo: payload
       };
   });
}

Messaging

Messages can be dispatched from the channelProvider to any channelClient or from any channelClient to the channelProvider. The workflow below shows the usage of a channel, in which a message is dispatched from a client to the provider for a specific topic and a corresponding acknowledgement is returned to the client:

  1. a message is dispatched over the channel for a specific topic to the corresponding provider
  2. the provider callback for that registered action is invoked
  3. the return value of the registered action is sent from the provider as a payload to the acknowledgement (ack) response or the error message to a negative-acknowledgement (nack)
  4. the ack or nack is routed back to the client and the promise resolves with this payload
    Each time a provider receives an incoming dispatch, it is required to send back a corresponding acknowledgement (can be undefined). Once a listener is registered for a particular action, it stays in place receiving and responding to incoming messages until it is removed. This messaging mechanism works exactly the same when messages are dispatched from the provider to a client. However, the provider has an additional publish method that sends messages to all connected clients.
    If a message is dispatched to an action that does not have a callback registered, an error is returned. This default error can be overwritten on the channelBus by setting a default action.

Example

// ...
   // previously created channelBus here could be a client or provider
   channelBus.register('topic1', (payload, identity) => {
       // register a callback for a given topic to which the sender can dispatch an action
       const somePayload = someAction(payload, identity);
       // The return value will be returned as a payload in response to the sender
       return somePayload;
   });

   channelBus.setDefaultAction((topic, payload, senderIdentity) => {
       // register a callback for topics that do not have a registered action
       return 'Dispatch will now resolve with this payload instead of reject on sender side.';
   });

   // if channelBus is a providerBus, there are three arguments to dispatch
   // and the first argument determines which client to target.
   const response = await channelBus.dispatch('topic2', 'payload');

   // the dispatch method returns a promise that resolves to the return value of the
   // register method's listener of the channel counterpart. 
   document.querySelector('#someId').innerText = response;
}

Did this page help you?