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 callingChannel.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:
- a message is dispatched over the channel for a specific topic to the corresponding provider
- the provider callback for that registered action is invoked
- 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)
- 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 thechannelBus
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;
}
Updated about 1 year ago