Receiving Notifications
Once a client is connected, it receives notifications through handlers registered on the SignalR connection. Each handler is bound to a specific notification type.
Handlers
A handler is a callback function bound to a specific notification type. When a notification of that type arrives, Notiway invokes the matching handler and passes the notification object. You define what happens inside — display a toast, update a list, refresh a chart, play a sound, or anything else your application needs.
connection.on("order-shipped", (notification) => {
// This runs every time an "order-shipped" notification arrives.
// The notification object contains the full payload from the producer.
});Register one handler per notification type. You can register as many types as your application needs.
What you can do inside a handler:
// Show a toast or banner
connection.on("payment-confirmed", (notification) => {
showToast(`Payment of $${notification.body.amount} confirmed`);
});
// Add to a notification list
connection.on("invoice-ready", (notification) => {
notifications.push(notification);
updateBadgeCount(notifications.length);
renderNotificationList(notifications);
});
// Update a live dashboard
connection.on("dashboard-update", (notification) => {
const { metric, value, timestamp } = notification.body;
updateChart(metric, value, timestamp);
});
// Update UI state (e.g. disable a button, show a progress bar)
connection.on("import-progress", (notification) => {
const { taskId, progress, status } = notification.body;
setProgressBar(taskId, progress);
if (status === "completed") {
enableDownloadButton(taskId);
}
});
// Trigger a browser notification
connection.on("urgent-alert", (notification) => {
new Notification(notification.body.title, {
body: notification.body.message
});
});Handlers are plain JavaScript (or C#, Dart, etc.) — there are no restrictions on what you can do inside them. Use the notification.body payload to drive your application logic however you need.
start(). Handlers for Tenant and Group audiences must be registered before calling Subscribe. Persisted notifications are replayed at the moment of connect or subscribe — any handler not yet registered will miss them.Notification Object
Every notification received by the client has the same structure, regardless of how it was routed or whether it was delivered live or replayed from persistence.
{
"id": "order-service-order-shipped-2025-01-15T10:00:00Z",
"type": "order-shipped",
"body": {
"orderId": "ORD-12345",
"message": "Your order has been shipped!"
},
"routing": {
"audienceType": 4,
"audienceValue": "user-123"
},
"metadata": {
"producer": "order-service",
"timeStamp": "2025-01-15T10:00:00Z",
"isPersisted": true
},
"audienceId": "user:user-123",
"isRead": false,
"isDeleted": false
}| Field | Type | Description |
|---|---|---|
id | string | Unique notification identifier |
type | string | Notification type — matches the handler name |
body | object | Custom payload defined by the producer |
routing | object | Audience targeting (type, value, optional tenantId) |
metadata | object | Producer name, timestamp, persistence settings |
audienceId | string | Audience identifier — needed for MarkAsRead and MarkAsDeleted |
isRead | boolean | Whether the notification has been marked as read (persisted only) |
isDeleted | boolean | Whether the notification has been marked as deleted (persisted only) |
Live vs Replayed Notifications
Notifications arrive through the same handler regardless of how they were delivered. Your handler code does not need to distinguish between the two.
Live — the client is connected when the notification is published. It arrives immediately through the matching handler.
Replayed — the client connects (or subscribes to an audience) and Notiway replays all persisted, non-expired notifications for that audience. Each replayed notification triggers the same handler as a live one would.
sequenceDiagram
participant Notiway
participant Client
Note over Client: Client connects
Client->>Notiway: Connect
Notiway->>Client: Replay persisted notifications
Client->>Client: Handlers fire for each replayed notification
Note over Client: While connected
Notiway->>Client: Live notification arrives
Client->>Client: Handler fires
This means you can use a single handler to build a notification list that includes both historical and live notifications:
const notifications = [];
connection.on("invoice-ready", (notification) => {
notifications.push(notification);
renderNotificationList(notifications);
});
await connection.start();Managing Persisted Notifications
For persisted notifications, clients can update read and deleted state. These changes are synced across all of the user’s connected devices.
// Mark as read
await connection.invoke("MarkAsRead", notification.audienceId, notification.id);
// Mark as unread
await connection.invoke("MarkAsRead", notification.audienceId, notification.id, false);
// Mark as deleted
await connection.invoke("MarkAsDeleted", notification.audienceId, notification.id);
// Restore (undelete)
await connection.invoke("MarkAsDeleted", notification.audienceId, notification.id, false);Cross-Device Sync
When a user marks a notification as read or deleted on one device, Notiway broadcasts the change to all of that user’s other connected clients. Listen for these events to keep the UI in sync:
connection.on("EventReadStatusChange", (change) => {
// change.eventId — the notification ID
// change.isRead — new read state
updateNotificationStatus(change.eventId, { isRead: change.isRead });
});
connection.on("EventDeletedStatusChange", (change) => {
// change.eventId — the notification ID
// change.isDeleted — new deleted state
updateNotificationStatus(change.eventId, { isDeleted: change.isDeleted });
});Delivery Rules
A notification is delivered to a client only when both conditions are true:
- The client has joined the target audience — automatic for Global, User, and Connection; requires
Subscribefor Tenant and Group. - The client has registered a handler for the notification type.
If the client is in the audience but has no handler registered, the notification is delivered but silently ignored.
For details on joining and leaving audiences, see Connection Management.