Messaging Events and Retrieving Notifications
This guide explains how to retrieve notifications from the Agglestone Messaging Service. You can retrieve individual notifications or paginated lists of notifications from a user’s inbox.
Setting Up the WebSocket Connection
Before you can receive notification events, you need to establish a WebSocket connection using SignalR:
// Install: npm install @microsoft/signalr@^10.0.0
import * as signalR from '@microsoft/signalr';
const tenantId = 'your-tenant-id'; // Get this from https://portal.agglestone.com
const apiBaseUrl = 'https://messaging.agglestone.com'; // Agglestone's messaging service URL
// Generate a unique client ID for this browser tab/window
const clientId = crypto.randomUUID();
// Create SignalR connection
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${apiBaseUrl}/tenant/${tenantId}/v1/hub?clientId=${encodeURIComponent(clientId)}`, {
accessTokenFactory: () => accessToken // JWT token from authentication service
})
.withAutomaticReconnect()
.build();
// Listen for notification events
connection.on("MessagingEvent", async (event) => {
// IMPORTANT: The WebSocket event only contains lightweight information
// You must fetch the full notification details using the REST API
if (event.type === 'Message') {
const notificationId = event.id;
// Fetch the full notification details
const notification = await getNotification(notificationId);
// Display the notification in your UI
displayNotification(notification);
}
});
// Start the connection
try {
await connection.start();
console.log('Connected to SignalR hub');
} catch (error) {
console.error('Connection error:', error);
}
Retrieving Individual Notifications
When you receive a WebSocket MessagingEvent for a notification, you need to fetch the full notification details from the REST API. The WebSocket event only contains lightweight information (notification ID, timestamp), so you must make an API call to get the complete notification data.
When to Retrieve Individual Notifications
You should retrieve an individual notification when:
- You receive a
MessagingEventthrough the WebSocket connection - You need to display a specific notification that was referenced by ID
- You want to refresh or verify notification details
Retrieving a Notification by ID
Endpoint: GET /tenant/{tenantId}/v1/Notifications/{notificationId}
Response:
{
"id": "notification-id-456",
"tenantId": "tenant-id-123",
"userId": "user-id-123",
"type": "SystemAnnouncement",
"subject": "System Maintenance Scheduled",
"message": "We will be performing scheduled maintenance on Saturday at 2 AM.",
"htmlContent": "<p>We will be performing <strong>scheduled maintenance</strong> on Saturday at 2 AM.</p>",
"summary": "Maintenance scheduled for Saturday",
"priority": "High",
"status": "Sent",
"createdAt": "2024-01-15T10:30:00Z",
"readAt": null,
"metadata": {
"actionUrl": "/maintenance"
}
}
Example: Retrieving an Individual Notification
The getNotification function is called from the MessagingEvent handler shown in the connection setup above:
async function getNotification(notificationId: string) {
const response = await fetch(
`${apiBaseUrl}/tenant/${tenantId}/v1/Notifications/${notificationId}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to fetch notification: ${response.statusText}`);
}
return await response.json();
}
Retrieving Paginated Notification Lists
When you need to load notification history, display an inbox interface, or implement infinite scroll, you should retrieve a paginated list of notifications.
When to Retrieve Paginated Notification Lists
You should retrieve paginated notification lists when:
- Loading inbox: When a user opens their inbox, you need to load recent notifications to display
- Implementing infinite scroll: As users scroll down in their inbox, you load older notifications
- Catching up after reconnection: When a user reconnects after being offline, you fetch notifications they missed
- Initial inbox load: When displaying the inbox for the first time, you load the most recent notifications
Note: It’s perfectly fine to retrieve notifications that you may have already loaded. You can avoid showing duplicates on the screen by checking the id field of each notification and only displaying notifications that haven’t been shown before. This is especially useful when loading older notifications for infinite scroll or when catching up after reconnection, as there may be overlap between the notifications you’ve already displayed and the new notifications you’re fetching.
Retrieving Notifications with Pagination
Endpoint: GET /tenant/{tenantId}/v1/Notifications
Query Parameters:
pageNumber(optional, default: 1): The page number to retrievepageSize(optional, default: 10, max: 50): The number of notifications per pagecreatedAfter(optional): ISO 8601 date/time – only return notifications created after this date
Response:
{
"data": [
{
"id": "notification-id-456",
"tenantId": "tenant-id-123",
"userId": "user-id-123",
"type": "SystemAnnouncement",
"subject": "System Maintenance Scheduled",
"message": "We will be performing scheduled maintenance on Saturday at 2 AM.",
"priority": "High",
"status": "Sent",
"createdAt": "2024-01-15T10:30:00Z",
"readAt": null
},
{
"id": "notification-id-455",
"tenantId": "tenant-id-123",
"userId": "user-id-123",
"type": "UserInvite",
"subject": "You've been invited",
"message": "You've been invited to join the team.",
"priority": "Normal",
"status": "Read",
"createdAt": "2024-01-15T09:00:00Z",
"readAt": "2024-01-15T09:05:00Z"
}
],
"pageNumber": 1,
"pageSize": 10,
"totalCount": 25,
"totalPages": 3,
"hasPreviousPage": false,
"hasNextPage": true
}
The response includes:
data: Array of notification objectspageNumber: Current page numberpageSize: Number of items per pagetotalCount: Total number of notifications in the inboxtotalPages: Total number of pageshasPreviousPage: Whether there are older notificationshasNextPage: Whether there are newer notifications
Example: Retrieving Paginated Notifications
async function getNotifications(
pageNumber: number = 1,
pageSize: number = 20,
createdAfter?: Date
) {
const params = new URLSearchParams({
pageNumber: pageNumber.toString(),
pageSize: pageSize.toString()
});
if (createdAfter) {
params.append('createdAfter', createdAfter.toISOString());
}
const response = await fetch(
`${apiBaseUrl}/tenant/${tenantId}/v1/Notifications?${params}`,
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to fetch notifications: ${response.statusText}`);
}
return await response.json();
}
// Load recent notifications when opening the inbox
async function loadInbox(pageNumber: number = 1) {
const result = await getNotifications(pageNumber, 20);
// Display notifications in your UI
result.data.forEach(notification => {
displayNotification(notification);
});
return result;
}
// Load older notifications for infinite scroll
async function loadOlderNotifications(currentPage: number) {
const result = await getNotifications(currentPage + 1, 20);
// Append older notifications to your inbox UI
result.data.forEach(notification => {
appendNotification(notification);
});
return result;
}
// Load notifications created after a specific date
async function loadNotificationsSince(date: Date) {
const result = await getNotifications(1, 50, date);
// Display notifications created since the specified date
result.data.forEach(notification => {
displayNotification(notification);
});
return result;
}
Best Practices
Loading Inbox
When a user opens their inbox, load the most recent notifications first:
// Load the most recent 20 notifications when opening the inbox
const notifications = await getNotifications(1, 20);
// Notifications are returned in reverse chronological order (newest first)
notifications.data.forEach(notification => {
displayNotification(notification);
});
Implementing Infinite Scroll
For infinite scroll, load older notifications as the user scrolls down:
let currentPage = 1;
let hasMoreNotifications = true;
async function loadMoreNotifications() {
if (!hasMoreNotifications) return;
const result = await getNotifications(currentPage + 1, 20);
if (result.data.length > 0) {
// Append older notifications
result.data.forEach(notification => {
appendNotification(notification);
});
currentPage++;
hasMoreNotifications = result.hasNextPage;
} else {
hasMoreNotifications = false;
}
}
Catching Up After Reconnection
When a user reconnects after being offline, fetch notifications they missed:
// Store the timestamp of the last notification you received
let lastNotificationTimestamp: Date | null = null;
// When reconnecting, fetch notifications since the last notification
async function catchUpAfterReconnect() {
if (lastNotificationTimestamp) {
const missedNotifications = await getNotifications(
1,
50,
lastNotificationTimestamp
);
// Display missed notifications
missedNotifications.data.forEach(notification => {
displayNotification(notification);
lastNotificationTimestamp = new Date(notification.createdAt);
});
}
}
Error Handling
Common errors you may encounter:
- 401 Unauthorized: Your access token is missing, invalid, or expired. Refresh your token and retry.
- 403 Forbidden: The tenant ID doesn’t match your authentication, or you’re trying to access another user’s notifications.
- 404 Not Found: The notification doesn’t exist, or you don’t have access to it.
- 400 Bad Request: Invalid query parameters (e.g., invalid date format, page size too large).
Always check the response status and handle errors appropriately in your application.
—
Ready to manage notifications? Check out Managing Notifications.