Files

6.8 KiB

Notification Service

A microservice for sending push notifications via web push protocol.

Overview

The notification service provides endpoints for sending web push notifications to subscribed clients. It uses the Web Push Protocol with VAPID (Voluntary Application Server Identification) for secure delivery of notifications.

Features

  • Web Push Notifications: Send push notifications to web browsers
  • VAPID Support: Secure authentication using VAPID keys
  • Subscription Management: Handles expired and invalid subscriptions
  • Token Authentication: Optional bearer token authentication
  • Error Handling: Automatic cleanup of invalid subscriptions

Prerequisites

  • Node.js (version specified in package.json)
  • VAPID key pair for web push authentication
  • Valid push subscriptions from client applications

Configuration

The service is configured via environment variables:

Variable Required Default Description
PORT Yes 8091 Port number for the service
SOURCE Yes - Source identifier for the service
AUTH_TOKEN No - Bearer token for API authentication
PUSH_PUBLIC_KEY No - VAPID public key for web push
PUSH_PRIVATE_KEY No - VAPID private key for web push
PUSH_SUBJECT No mailto:hey@huly.io VAPID subject (email or URL)

VAPID Keys

To generate VAPID keys, you can use the web-push library:

npx web-push generate-vapid-keys

This will output a public and private key pair that should be set in the environment variables.

Installation

npm install

Running the Service

Development

rushx run-local

Docker

docker build -t notification-service .
docker run -p 8091:8091 \
  -e SOURCE=notification \
  -e PUSH_PUBLIC_KEY=your_public_key \
  -e PUSH_PRIVATE_KEY=your_private_key \
  notification-service

API Endpoints

POST /web-push

Sends web push notifications to subscribed clients.

Authentication

  • Header: Authorization: Bearer <token> (if AUTH_TOKEN is configured)

Request Body

{
  "data": {
    "title": "Notification Title",
    "body": "Notification message",
    "icon": "/icon.png",
    "badge": "/badge.png",
    "tag": "notification-tag",
    "url": "/target-url"
  },
  "subscriptions": [
    {
      "_id": "subscription-id",
      "endpoint": "https://fcm.googleapis.com/fcm/send/...",
      "keys": {
        "p256dh": "client-public-key",
        "auth": "client-auth-secret"
      }
    }
  ]
}

Response

{
  "result": ["subscription-id-1", "subscription-id-2"]
}

The result array contains IDs of subscriptions that failed due to:

  • Expired subscriptions
  • Unregistered subscriptions
  • Invalid subscriptions

These subscription IDs should be removed from your database.

Error Responses

  • 400 Bad Request: Missing data or subscriptions in request body
  • 401 Unauthorized: Invalid or missing auth token (when auth is enabled)
  • 500 Internal Server Error: Server error during processing

Push Data Format

The data object supports the following properties:

Property Type Required Description
title string Yes Notification title
body string No Notification body text
icon string No URL to notification icon
badge string No URL to notification badge
tag string No Tag for grouping notifications
url string No URL to open when notification is clicked
data object No Custom data payload

Client Integration

Subscribing to Push Notifications

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

// Subscribe to push notifications
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: 'your-vapid-public-key'
});

// Send subscription to your server
await fetch('/api/subscribe', {
  method: 'POST',
  body: JSON.stringify(subscription),
  headers: { 'Content-Type': 'application/json' }
});

Service Worker (sw.js)

self.addEventListener('push', event => {
  const data = event.data ? event.data.json() : {};
  
  const options = {
    body: data.body,
    icon: data.icon,
    badge: data.badge,
    tag: data.tag,
    data: { url: data.url }
  };

  event.waitUntil(
    self.registration.showNotification(data.title, options)
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  
  if (event.notification.data?.url) {
    event.waitUntil(
      clients.openWindow(event.notification.data.url)
    );
  }
});

Error Handling

The service automatically handles common web push errors:

  • Expired subscriptions: Automatically identified and returned in response
  • Invalid endpoints: Subscriptions with invalid endpoints are flagged
  • Unregistered subscriptions: Previously valid subscriptions that are no longer active

Applications should monitor the response and remove failed subscription IDs from their database.

Monitoring

The service logs the following events:

  • Service startup and VAPID configuration
  • Authentication failures
  • Push notification errors
  • Subscription cleanup events

Security

  • VAPID Authentication: All push messages are signed with VAPID keys
  • Token Authentication: Optional bearer token authentication for API access
  • HTTPS Required: Web Push Protocol requires HTTPS in production
  • Origin Validation: Push subscriptions are tied to specific origins

Development

Project Structure

src/
├── main.ts          # Main application entry point
├── config.ts        # Configuration management
├── server.ts        # Express server setup
└── types.ts         # TypeScript type definitions

Troubleshooting

Common Issues

  1. "No VAPID keys configured"

    • Ensure PUSH_PUBLIC_KEY and PUSH_PRIVATE_KEY are set
    • Verify keys are valid VAPID keys
  2. "Invalid auth token"

    • Check AUTH_TOKEN environment variable
    • Verify bearer token in Authorization header
  3. Push notifications not delivered

    • Verify subscription is still valid
    • Check browser developer tools for service worker errors
    • Ensure HTTPS is used in production
  4. High subscription failure rate

    • Check if users are unsubscribing
    • Verify subscription objects are properly formatted
    • Monitor browser console for push registration errors