Skip to content

Latest commit

Β 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Web Push Notifications β€” Azure Notification Hubs

Full-stack web push notification system with two backend options (Node.js / Python) and a React testing dashboard.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   React Frontend    β”‚       β”‚   React Frontend    β”‚
β”‚   (User A browser)  β”‚       β”‚   (User B browser)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ subscription                 β”‚ subscription
         β–Ό                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Backend API                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Node.js (Express)β”‚  β”‚  Python (FastAPI)        β”‚ β”‚
β”‚  β”‚  :4000            β”‚  β”‚  :8001                   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚           β”‚                     β”‚                    β”‚
β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚                     β–Ό                                β”‚
β”‚        Azure Notification Hubs                      β”‚
β”‚        (installations + send)                       β”‚
β”‚                     β”‚                                β”‚
β”‚                     β–Ό                                β”‚
β”‚              Browser Push (FCM/APNs)                β”‚
β”‚                     β”‚                                β”‚
β”‚                     β–Ό                                β”‚
β”‚              Service Worker β†’ Notification           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Project Structure

p-not/
β”œβ”€β”€ node-backend/                    # Node.js backend (Express)
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ index.js            # Server entry
β”‚   β”‚   β”œβ”€β”€ config.js           # Environment config
β”‚   β”‚   β”œβ”€β”€ routes.js           # API routes
β”‚   β”‚   β”œβ”€β”€ notificationService.js  # Azure NH client wrapper
β”‚   β”‚   └── generate-vapid.js   # VAPID key generator
β”‚   β”œβ”€β”€ .env.example
β”‚   └── package.json
β”‚
β”œβ”€β”€ python-backend/             # Python backend (FastAPI)
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ main.py             # FastAPI app + middleware + logging
β”‚   β”‚   β”œβ”€β”€ config.py           # Pydantic settings
β”‚   β”‚   β”œβ”€β”€ auth.py             # HMAC-SHA256 + SAS token generation
β”‚   β”‚   β”œβ”€β”€ client.py           # NotificationHubsClient (REST API)
β”‚   β”‚   └── routes.py           # All API endpoints
β”‚   β”œβ”€β”€ requirements.txt
β”‚   └── .env
β”‚
β”œβ”€β”€ frontend/                   # React + Vite
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ App.jsx             # Dashboard with scenario cards
β”‚   β”‚   β”œβ”€β”€ main.jsx            # Entry point
β”‚   β”‚   β”œβ”€β”€ usePushNotifications.js  # Push hook (all API calls)
β”‚   β”‚   └── components/
β”‚   β”‚       β”œβ”€β”€ Modal.jsx            # Base modal + form fields
β”‚   β”‚       β”œβ”€β”€ SubscribeModal.jsx   # Subscribe as User A/B
β”‚   β”‚       β”œβ”€β”€ SendBasicModal.jsx   # Phase 2.1 β€” tag-based send
β”‚   β”‚       β”œβ”€β”€ SendRichModal.jsx    # Phase 3 β€” icon, image, actions
β”‚   β”‚       β”œβ”€β”€ SendTargetedModal.jsx # Phase 2.2, 4.3, 4.4 β€” TTL, urgency
β”‚   β”‚       β”œβ”€β”€ BroadcastModal.jsx   # Phase 2.3 β€” all subscribers
β”‚   β”‚       β”œβ”€β”€ ScheduledModal.jsx   # Phase 7 β€” future delivery
β”‚   β”‚       └── DebugPanel.jsx       # Real-time debug log
β”‚   β”œβ”€β”€ public/
β”‚   β”‚   └── sw.js               # Service worker (rich notification support)
β”‚   β”œβ”€β”€ index.html
β”‚   └── vite.config.js
β”‚
β”œβ”€β”€ package.json                # Root scripts
β”œβ”€β”€ .gitignore
└── README.md

Quick Start

Prerequisites

  • Node.js 18+
  • Python 3.12+ (for Python backend)
  • Azure Notification Hub (Free tier works)
  • Modern browser (Chrome, Firefox, Edge β€” HTTPS or localhost)

1. Generate VAPID Keys

cd node-backend
npm install
npm run generate-vapid

Copy the output β€” you'll need both keys.

2. Configure Azure Notification Hub

  1. Create a Notification Hub in Azure Portal
  2. Go to Notification Hub β†’ Access Policies β†’ copy DefaultFullSharedAccessSignature connection string
  3. Go to Browser Push (Web) β†’ set:
    • Public Key: from step 1
    • Private Key: from step 1
    • Subject: mailto:you@example.com

3. Configure Environment

Node.js backend (node-backend/.env):

AZURE_NOTIFICATION_HUB_CONNECTION_STRING=Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=<key>;EntityPath=<hub-name>
AZURE_NOTIFICATION_HUB_NAME=<hub-name>
VAPID_PUBLIC_KEY=<from step 1>
VAPID_PRIVATE_KEY=<from step 1>
VAPID_SUBJECT=mailto:you@example.com
PORT=4000
FRONTEND_URL=http://localhost:5173

Python backend (python-backend/.env):

AZURE_NOTIFICATION_HUB_CONNECTION_STRING=Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=<key>
AZURE_NOTIFICATION_HUB_NAME=<hub-name>
VAPID_PUBLIC_KEY=<from step 1>
VAPID_PRIVATE_KEY=<from step 1>
VAPID_SUBJECT=mailto:you@example.com
PORT=8001
FRONTEND_URL=http://localhost:5173

4. Install Dependencies

# Node backend
cd node-backend && npm install

# Python backend
cd python-backend && pip install -r requirements.txt

# Frontend
cd frontend && npm install

5. Run

Option A β€” Python backend + React frontend:

# Terminal 1
cd python-backend
python -m uvicorn app.main:app --reload --port 8001

# Terminal 2
cd frontend
npm run dev

Option B β€” Node backend + React frontend:

# Terminal 1
cd    node-backend
npm run dev    # port 4000

# Terminal 2
cd frontend
npm run dev    # port 5173

If using Node backend, update API_BASE in frontend/src/usePushNotifications.js to http://localhost:4000/api.

6. Test

  1. Open http://localhost:5173 in Chrome
  2. Click Subscribe β†’ enter user-a β†’ subscribe
  3. Open http://localhost:5173 in a second browser/incognito
  4. Click Subscribe β†’ enter user-b β†’ subscribe
  5. Use the dashboard scenario cards to test each feature

Testing Scenarios

Phase Scenario Description
Core Subscribe Register browser as User A or User B
2.1 Send to User Tag-based send to user:user-a
2.2 Tag Expression Multi-tag filtering (user:user-a AND web)
2.3 Broadcast Send to ALL subscribers via all tag
3.x Rich Notification Custom icon, image, action buttons, tag collapse
4.3 TTL Notification expires after N seconds
4.4 Urgency Priority levels: low / normal / high
4.1 410 GONE Auto-cleanup of expired subscriptions
5.1 Registrations List all Azure installations (debug)
7.1 Schedule Future delivery (Standard SKU only)
7.2 Cancel Cancel a scheduled notification

API Reference

Core

Method Endpoint Description
GET /api/vapid-public-key Returns VAPID public key for browser subscription
POST /api/save-subscription Register browser push subscription as Azure installation
DELETE /api/unsubscribe Remove installation from Azure

Send Notifications

Method Endpoint Body Description
POST /api/send { userId, title, body, url? } Send to specific user
POST /api/send/rich { userId, title, body, icon?, image?, actions?, tag?, silent? } Rich notification
POST /api/send/targeted { tagExpression, title, body, ttl?, urgency? } Send with tag expression + TTL + urgency
POST /api/broadcast { title, body, url?, ttl?, urgency? } Send to all subscribers
POST /api/send/scheduled { userId, title, body, scheduledTime } Schedule future delivery (Standard SKU)
DELETE /api/scheduled/{id} β€” Cancel scheduled notification

Analytics & Debug

Method Endpoint Description
GET /api/registrations List all installations (debug)
GET /api/installations/{id} Get specific installation
GET /api/outcome/{id} Get delivery outcome for a sent notification
GET /api/feedback Get PNS feedback container URL

Service Worker

The service worker (frontend/public/sw.js) handles:

  • Push events β€” displays notification with title, body, icon, image, badge, actions
  • Click events β€” opens the url from the notification payload
  • Tag collapse β€” notifications with same tag replace previous ones
  • Silent mode β€” silent: true suppresses sound/vibration

Azure Notification Hubs β€” How It Works

Installation Model

When a user subscribes:

  1. Browser generates a push subscription (endpoint + keys)
  2. Frontend sends it to the backend
  3. Backend creates an installation in Azure with:
    • installationId β€” UUID
    • platform β€” "browser"
    • pushChannel β€” endpoint, p256dh, auth
    • tags β€” [user:<id>, all, web]
    • expirationTime β€” 30 days

Send Model

When sending a notification:

  1. Backend calls Azure REST API (POST /messages)
  2. Azure matches tags to installations
  3. Azure forwards to browser push service (FCM)
  4. FCM delivers to the service worker

Tag System

Tags enable targeting:

Tag Purpose
user:<id> Target a specific user
all Target everyone (broadcast)
web Target web browsers only

Tag expressions: user:user-a, user:user-a AND web, all.

Production Checklist

  • HTTPS β€” push requires HTTPS (localhost is exempt)
  • Auth middleware β€” replace userId from request body with JWT/session
  • Connection string security β€” use DefaultSendSharedAccessSignature (not Full)
  • 410 handling β€” auto-delete expired installations
  • Rate limiting β€” prevent abuse of send endpoints
  • TTL β€” set appropriate expiration for time-sensitive notifications
  • Error monitoring β€” log Azure NH errors with tracking IDs
  • Registration cleanup β€” periodically remove expired installations

Troubleshooting

Problem Cause Fix
401 Unauthorized Wrong connection string Add EntityPath=<hub-name> to connection string
404 Hub not found Wrong hub name Check AZURE_NOTIFICATION_HUB_NAME matches Azure Portal
400 Bad Request Invalid JSON body Check request Content-Type is application/json
Notification not received VAPID not configured Set VAPID keys in Azure Portal β†’ Browser Push
successCount: 0 No matching installations Check tags match between installation and send
Permission denied User blocked notifications Detect via Notification.permission === "denied"

Tech Stack

Layer Technology
Frontend React 18, Vite, Service Workers
Node backend Express, @azure/notification-hubs v2
Python backend FastAPI, httpx, Azure REST API
Push service Azure Notification Hubs
Browser push FCM (Chrome/Firefox/Edge), APNs (Safari)

License

MIT

About

Full-stack web push notification system with two backend options (Node.js / Python) and a React testing dashboard.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages