SSE Client
The SSE Client
is a simple SDK that helps to simplify writing server-sent events (SSE) code on the frontend, as well
as providing additional functionalities on top of it. It integrates with the RTUS-SEH
service to enable real-time
updates from the backend to the frontend via server-sent events.
Server-sent events works in an identical way to websockets when it comes to handling incoming events, although there are some differences (e.g. it is a one-way connection). For more information, MDN has excellent documentation on server-sent events.
Installation
The RTUS-SEH
service must be runninng for SSE Client to connect to.
This documentation assumes that you have already have knowledge on how RTUS
(Real-time Update Service) works. If not,
visit the RTUS documentation page.
Before proceeding, it is assumed that you have already handled all your access configuration - review the docs if you're having issues with accessing the necessary packages.
Install via command line
npm install @mssfoobar/sse-client
Install via package.json
Add the dependency below into the dependencies
object in your package.json
, and save the file. Afterwards, run
npm i
or npm install
to install the sse-client.
"@mssfoobar/sse-client": "*"
Configuration Object
SSESubscribeClient
interface BaseSSEClientConfig<T> {
readonly tenantId: string;
readonly mapName: string;
readonly domainURL: string;
readonly init?: boolean;
readonly maxReconnectAttempts?: number;
readonly baseReconnectDelay?: number;
readonly eventHandlers?: { [K in EventName]?: (data: T) => void };
readonly refreshUrl?: string;
readonly onAnyEvent?: (eventName: string, data: T) => void;
readonly onConnected?: (event: Event) => void;
readonly onDisconnected?: (error?: Error) => void;
readonly OnReconnecting?: (attempt: number) => void;
readonly onError?: (error: Error) => void;
}
Only tenantId
, mapName
, and domainURL
are mandatory properties when initializing the SSESubScribeClient class, as
they are required to form the URL to call the RTUS SEH.
Initialization Example
const baseSehUrl = env.PUBLIC_RTUS_SEH_URL;
onMount(() => {
const domainURL = `${baseSehUrl}`;
const tenantId = data.user.active_tenant.tenant_id;
const mapName = "ian";
const userId = data.user.sub;
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
mapName,
userId,
init: false,
maxReconnectAttempts: 10,
baseReconnectDelay: 1000,
eventHandlers: {
Added: (data) => {
const { unread_count, message } = data;
logger.debug("Added.");
},
Updated: (data) => {
const { unread_count, message } = data;
logger.debug("Updated.");
},
Removed: (data) => {
const { unread_count, message } = data;
logger.debug("Removed.");
},
Timeout: (data) => {
const { unread_count, message } = data;
logger.debug("Timeout.");
},
},
onAnyEvent: (eventName, data) => {
logger.debug(`Received event ${eventName}`, data);
// Additional custom handling of the event
},
onConnected: () => {
logger.debug("Connected to SSE");
},
onDisconnected: () => {
logger.debug("Disconnected to SSE");
},
OnReconnecting: () => {
logger.debug("Attempting to reconnect to SSE");
},
OnError: (error) => {
logger.debug("Error handling message here.", error);
},
});
});
For map-based
mapping for the GIS service, userId is not required to form the URL
for RTUS-SEH.
API
maxReconnectAttempts
The maxReconnectAttempts
property will determine the maximium amount of time the sse-client will attempt to reconnect
to RTUS SEH. If this property is not included in the initialization of the SSESubscribeClient class, it will have a
default value of 10.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
mapName,
userId,
init: false,
maxReconnectAttempts: 10,
baseReconnectDelay: 1000,
.
.
.
});
baseReconnectDelay
The baseReconnectDelay
property will determine the delay between reconnection attempts when the SSE client tries to
reestablish a connection with the RTUS SEH after being disconnected.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
mapName,
userId,
init: false,
maxReconnectAttempts: 10,
baseReconnectDelay: 1000,
.
.
.
});
However, as its name suggests, it is only the base
reconnection delay time. Exponential backoff and "jitter," or
randomness, is also integrated within the calculation of the reconnection delay time to mitigate the potential of a
thunder herding problem.
The exponential backoff algorithm implemented takes reference from AWS's "exponential backoff with full jitter" algorithm, which can be found here.
eventHandlers
The RTUS-SEH service currently supports 4 custom events: Added
, Removed
, Updated
and Timeout
. With the
eventHandlers
property, the application will be able to respond to the different custom events respectively.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
mapName,
userId,
init: false,
maxReconnectAttempts: 10,
baseReconnectDelay: 1000,
// this example shows 2 eventHandlers for the "Added" and "Updated" events
eventHandlers: {
Added: (data) => {
const { unread_count, message } = data;
updateNotificationStore([message], unread_count, false, "sse");
},
Updated: (data) => {
const { unread_count, message } = data;
updateNotificationStore([message], unread_count, false, "sse");
},
},
});
refreshUrl
The refreshUrl
property allows the application to set the specific URL endpoint responsible for token refresh. If left
empty, the default url will be the default Web Base
token refresh URL.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
mapName,
userId,
init: false,
maxReconnectAttempts: 10,
baseReconnectDelay: 1000,
refreshUrl: '/api/to/refresh/token'
.
.
.
});
onAnyEvent
The onAnyEvent
property is a catch-all handler intended for common logic that applies to all events, regardless of
the specific event type.
const domainURL = `${baseSehUrl}`;
const tenantId = data.user.active_tenant.tenant_id;
const mapName = "ian";
const userId = data.user.sub;
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
.
.
.
onAnyEvent: (eventName, data) => {
logger.debug(`Received event ${eventName}`, data);
// Additional custom handling of the event
},
.
.
});
onConnected
The onConnected
property allows the application to handle the EventSource
open event when the event source is opened.
const domainURL = `${baseSehUrl}`;
const tenantId = data.user.active_tenant.tenant_id;
const mapName = "ian";
const userId = data.user.sub;
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
.
.
.
onConnected: () => {
logger.debug("Connected to SSE");
},
.
.
.
});
onDisconnected
The onDisconnected
property allows the the application to to handle and execute custom logic when the connection to
the SSE server is lost or closed.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
.
.
.
onConnected: () => {
logger.debug("Connected to SSE");
},
onDisconnected: () => {
logger.debug("Disconnected to SSE");
}
.
.
.
});
onReconnecting
The onReconnecting
property allows the application to handle and execute custom logic when the sse-client is
attempting to reconnect to the RTUS-SEH service.
sseClient = new SSESubscribeClient({
domainURL,
tenantId,
.
.
.
onConnected: () => {
logger.debug("Connected to SSE");
},
onDisconnected: () => {
logger.debug("Disconnected to SSE");
},
onReconnecting: () => {
logger.debug("Attempting to reconnect to SSE");
},
.
.
.
});
onError
The onError
property allows the application to handle and execute custom logic when an error is encountered, (e.g.
logging the error or displaying a user-friendly message).
const sseClient = new SSESubscribeClient({
domainURL,
tenantId,
.
.
.
onConnected: () => {
logger.debug("Connected to SSE");
},
onDisconnected: () => {
logger.debug("Disconnected to SSE");
},
onReconnecting: () => {
logger.debug("Attempting to reconnect to SSE");
},
onError: (error) => {
logger.debug("There is an error.");
}
.
.
.
});