Use Cases

Common patterns for using Notiway in your applications.

Regular Notifications

The simplest use case. Your backend publishes a notification when something happens, and the client displays it to the user in real time.

Examples: order shipped, new message received, payment confirmed, user mentioned in a comment.

  sequenceDiagram
    participant Backend
    participant Notiway
    participant Client

    Backend->>Notiway: Publish notification
    Notiway->>Client: Push over WebSocket
    Client->>Client: Display to user

Producer side:

{
  "id": "order-service-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",
    "isPersisted": false
  }
}

Client side:

connection.on("order-shipped", (notification) => {
    showToast(notification.body.message);
});

Set isPersisted: true if you want offline users to receive the notification when they reconnect.


Notification Hub

A notification center where users can browse, read, and manage their notifications. This pattern uses persistence to store notifications and replay them on connect.

Examples: in-app notification inbox, activity feed, alert center.

  sequenceDiagram
    participant Backend
    participant Notiway
    participant Client

    Backend->>Notiway: Publish persisted notification
    Notiway->>Client: Push over WebSocket
    Client->>Client: Add to notification list

    Note over Client: User reconnects later
    Client->>Notiway: Connect
    Notiway->>Client: Replay missed notifications
    Client->>Client: Populate notification list

    Note over Client: User interacts
    Client->>Notiway: MarkAsRead

Producer side: enable persistence and set a TTL.

{
  "id": "billing-invoice-ready-2025-01-15",
  "type": "invoice-ready",
  "body": {
    "invoiceId": "INV-789",
    "amount": 149.99,
    "currency": "USD"
  },
  "routing": {
    "audienceType": 4,
    "audienceValue": "user-123"
  },
  "metadata": {
    "producer": "billing-service",
    "isPersisted": true,
    "persistedTTL": "2025-02-15T00:00:00Z"
  }
}

Client side: build a notification list and handle read/delete actions.

const notifications = [];

// Collect notifications (both live and replayed on connect)
connection.on("invoice-ready", (notification) => {
    notifications.push(notification);
    updateNotificationBadge(notifications.length);
});

// Mark as read when user opens the notification
async function onNotificationClick(notification) {
    await connection.invoke("MarkAsRead", notification.audienceId, notification.id);
}

// Mark as deleted when user dismisses
async function onNotificationDismiss(notification) {
    await connection.invoke("MarkAsDeleted", notification.audienceId, notification.id);
}

// Keep all devices in sync
connection.on("EventReadStatusChange", (change) => {
    updateNotificationStatus(change.eventId, { isRead: change.isRead });
});

connection.on("EventDeletedStatusChange", (change) => {
    updateNotificationStatus(change.eventId, { isDeleted: change.isDeleted });
});

Status changes are synced across all of the user’s connected devices automatically.


Progress Events

Real-time progress tracking for long-running operations. The backend publishes progress updates to a specific user or connection, and the client renders a progress indicator.

Examples: file processing, deployment pipeline, data import/export, report generation.

  sequenceDiagram
    participant Backend
    participant Notiway
    participant Client

    Client->>Backend: Start long-running task
    Backend->>Client: Return task ID

    loop Progress updates
        Backend->>Notiway: Publish progress event
        Notiway->>Client: Push over WebSocket
        Client->>Client: Update progress bar
    end

    Backend->>Notiway: Publish completion event
    Notiway->>Client: Push over WebSocket
    Client->>Client: Show completed state

Producer side: send periodic progress updates.

{
  "id": "import-service-progress-2025-01-15T10:00:05Z",
  "type": "import-progress",
  "body": {
    "taskId": "import-456",
    "progress": 45,
    "status": "processing",
    "message": "Processing row 4500 of 10000..."
  },
  "routing": {
    "audienceType": 4,
    "audienceValue": "user-123"
  },
  "metadata": {
    "producer": "import-service",
    "isPersisted": false
  }
}

Client side: register the handler before starting the task.

// Register handler for progress updates
connection.on("import-progress", (notification) => {
    const { taskId, progress, status, message } = notification.body;
    updateProgressBar(taskId, progress);
    updateStatusText(taskId, message);

    if (status === "completed") {
        showCompletionMessage(taskId);
    }
});

// Start the task
const response = await fetch("/api/imports", { method: "POST", body: data });
const { taskId } = await response.json();
showProgressBar(taskId);
Progress events are typically short-lived and don’t need persistence. Set isPersisted: false to avoid storing transient updates.

For progress scoped to a specific page or view, consider routing to the Connection audience (audienceType: 5) so only the tab that initiated the task receives updates.


System Announcements

Broadcast messages to all connected users at once. Ideal for platform-wide communication where every user needs to see the same message.

Examples: scheduled maintenance alerts, new feature announcements, policy changes, incident status updates.

  sequenceDiagram
    participant Admin
    participant Notiway
    participant Client A
    participant Client B
    participant Client N

    Admin->>Notiway: Publish global announcement
    par Broadcast
        Notiway->>Client A: Push over WebSocket
        Notiway->>Client B: Push over WebSocket
        Notiway->>Client N: Push over WebSocket
    end
    Client A->>Client A: Display banner
    Client B->>Client B: Display banner
    Client N->>Client N: Display banner

Producer side: use Global routing to reach all connected clients.

{
  "id": "platform-maintenance-2025-03-10T08:00:00Z",
  "type": "system-announcement",
  "body": {
    "title": "Scheduled Maintenance",
    "message": "The platform will be unavailable on March 12 from 02:00 to 04:00 UTC.",
    "severity": "warning",
    "actionUrl": "/status"
  },
  "routing": {
    "audienceType": 1,
    "audienceValue": "global"
  },
  "metadata": {
    "producer": "admin-service",
    "isPersisted": true,
    "persistedTTL": "2025-03-12T04:00:00Z"
  }
}

Client side: display a persistent banner or modal.

connection.on("system-announcement", (notification) => {
    const { title, message, severity, actionUrl } = notification.body;

    showAnnouncementBanner({
        title,
        message,
        severity,  // "info" | "warning" | "critical"
        actionUrl,
        onDismiss: () => {
            connection.invoke("MarkAsRead", notification.audienceId, notification.id);
        }
    });
});
Set isPersisted: true so users who are offline see the announcement when they reconnect. Set the persistedTTL to match when the announcement becomes irrelevant (e.g., after the maintenance window ends).

To scope announcements to a single tenant, use Tenant routing (audienceType: 2) instead of Global.


Live Dashboard

Push real-time data updates to dashboards and monitoring views. Instead of clients polling your API, your backend publishes state changes and Notiway pushes them instantly to every viewer.

Examples: analytics dashboards, order monitoring, server health panels, KPI trackers.

  sequenceDiagram
    participant Backend
    participant Notiway
    participant Dashboard A
    participant Dashboard B

    Note over Dashboard A,Dashboard B: Users open the dashboard
    Dashboard A->>Notiway: Subscribe to "dashboard-ops" group
    Dashboard B->>Notiway: Subscribe to "dashboard-ops" group

    loop Data changes
        Backend->>Notiway: Publish dashboard update
        par Push to viewers
            Notiway->>Dashboard A: Push over WebSocket
            Notiway->>Dashboard B: Push over WebSocket
        end
        Dashboard A->>Dashboard A: Update charts
        Dashboard B->>Dashboard B: Update charts
    end

    Note over Dashboard A: User leaves the page
    Dashboard A->>Notiway: Unsubscribe from "dashboard-ops"

Client side: subscribe to the dashboard group when the user opens the page, unsubscribe when they leave.

// When the dashboard page mounts
await connection.invoke("Subscribe", {
    audienceType: 3,
    audienceValue: "dashboard-ops"
});

// Handle incoming updates
connection.on("dashboard-update", (notification) => {
    const { metric, value, timestamp } = notification.body;
    updateChart(metric, value, timestamp);
});

// When the user leaves the dashboard page
await connection.invoke("Unsubscribe", {
    audienceType: 3,
    audienceValue: "dashboard-ops"
});

Producer side: publish updates to the group whenever metrics change.

{
  "id": "metrics-service-dashboard-2025-03-10T10:05:00Z",
  "type": "dashboard-update",
  "body": {
    "metric": "active-users",
    "value": 1284,
    "timestamp": "2025-03-10T10:05:00Z"
  },
  "routing": {
    "audienceType": 3,
    "audienceValue": "dashboard-ops"
  },
  "metadata": {
    "producer": "metrics-service",
    "isPersisted": false
  }
}
Dashboard updates are ephemeral — set isPersisted: false since viewers always want the latest state, not old snapshots. If you need viewers to see the current state on connect, have your dashboard page fetch the initial data from your API and let Notiway handle the live updates after that.

To scope the dashboard to a specific tenant, add tenantId to the routing and subscribe with the same tenant ID on the client.