CloudSignal Docs
Use Cases

Push Notifications

Trigger native push notifications from MQTT messages using service workers.

Push notifications reach users even when your application is not in the foreground. By combining MQTT subscriptions with the Web Push API and service workers, you can convert incoming MQTT messages into native browser notifications.

Architecture

A service worker runs in the background and maintains an MQTT connection. When a message arrives on a notification topic, the service worker triggers the browser's Push API to display a native notification.

CloudSignal ──MQTT message──▶ Service Worker ──Push API──▶ Native Notification

                                     └── Runs even when tab is inactive

Push notifications require HTTPS and explicit user permission. Always request permission in response to a user action, not on page load.

How It Works

  1. User grants permission for notifications in the browser
  2. Service worker registers and establishes an MQTT connection
  3. MQTT message arrives on the user's notification topic
  4. Service worker creates a native push notification using the Notification API
  5. User clicks the notification to open the relevant page

Implementation

Register the Service Worker

// main.js
async function setupPushNotifications() {
  // Request notification permission
  const permission = await Notification.requestPermission()
  if (permission !== 'granted') return

  // Register service worker
  const registration = await navigator.serviceWorker.register(
    '/notification-worker.js'
  )

  // Send MQTT credentials to the service worker
  registration.active.postMessage({
    type: 'INIT_MQTT',
    config: {
      host: 'wss://connect.cloudsignal.app:18885/',
      username: 'user-alice',
      token: 'your-token',
      topic: 'notify/alice'
    }
  })
}

Service Worker with MQTT

// notification-worker.js
importScripts('https://cdn.cloudsignal.com/mqtt-worker.min.js')

let mqttClient = null

self.addEventListener('message', (event) => {
  if (event.data.type === 'INIT_MQTT') {
    const { host, username, token, topic } = event.data.config

    mqttClient = CloudSignalMQTT.connect({
      host,
      username,
      password: token
    })

    mqttClient.subscribe(topic, { qos: 1 })

    mqttClient.on('message', (topic, payload) => {
      const data = JSON.parse(payload.toString())
      showNotification(data)
    })
  }
})

function showNotification(data) {
  self.registration.showNotification(data.title, {
    body: data.body,
    icon: '/icons/notification-icon.png',
    badge: '/icons/badge-icon.png',
    data: { url: data.action?.url || '/' },
    tag: data.id // Prevents duplicate notifications
  })
}

// Handle notification click
self.addEventListener('notificationclick', (event) => {
  event.notification.close()

  event.waitUntil(
    clients.openWindow(event.notification.data.url)
  )
})

Trigger from Server

Use the same server-side publish pattern as in-app notifications:

await fetch('https://api.cloudsignal.com/v2/publish', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_your_secret_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    topic: 'notify/alice',
    payload: JSON.stringify({
      id: 'notif-001',
      title: 'New order received',
      body: 'Order #4521 needs review',
      action: { url: '/orders/4521' }
    }),
    qos: 1
  })
})

Combining In-App and Push

A common pattern is to use the same notification topic for both in-app and push notifications. The in-app handler displays a toast when the app is visible, and the service worker shows a native notification when it is not.

// In your app code
client.on('message', (topic, payload) => {
  if (document.visibilityState === 'visible') {
    showToast(JSON.parse(payload.toString()))
  }
  // Service worker handles push when tab is not visible
})

Next Steps

On this page