A multi-device web push notification backend service built with Node.js and TypeScript. This service enables applications to send push notifications to users across multiple devices and browsers simultaneously.
- Multi-Device Support: Users can subscribe from multiple devices/browsers
- Cross-Browser Compatibility: Works with Chrome, Firefox, Safari, and Edge
- RESTful API: Clean API design with OpenAPI validation
- Automatic Cleanup: Invalid subscriptions are automatically removed
- Detailed Reporting: Success/failure tracking per user and device
- Docker Support: Easy deployment with Docker and Docker Compose
- Node.js 18+ or 22+
- Yarn package manager
- MongoDB 6+ or 8+
- Docker and Docker Compose (for containerized setup)
git clone <repository-url>
cd push-notification-serviceyarn installVAPID (Voluntary Application Server Identification) keys are required for web push notifications:
# Install web-push globally
yarn global add web-push
# Generate VAPID keys
web-push generate-vapid-keysSave the generated public and private keys - you'll need them for configuration.
Create a .env file in the project root:
# Copy the example file
cp .env.example .envEdit .env with your configuration:
NODE_ENV=development
PORT=3000
MONGODB_URI=mongodb://localhost:27017/pushnotifications
VAPID_PUBLIC_KEY=<your-generated-public-key>
VAPID_PRIVATE_KEY=<your-generated-private-key>
VAPID_EMAIL=mailto:admin@example.com
LOG_LEVEL=infoEdit public/index.html and replace the VAPID public key with your generated key:
// Line 23 in public/index.html
const VAPID_PUBLIC_KEY = 'YOUR_GENERATED_PUBLIC_KEY_HERE';This is required for the browser to subscribe to push notifications.
- Start MongoDB (if not using Docker):
# Using MongoDB installed locally
mongod- Run the development server:
yarn devThe service will be available at http://localhost:3000
- Build and start all services:
docker-compose up --buildThis will start:
- The push notification service on port 3000
- MongoDB on port 27017
- Stop services:
docker-compose down- Open your browser and navigate to:
http://localhost:3000 - The test page allows you to:
- Subscribe the current device to push notifications
- View all subscriptions for a user
- Send test notifications to all your devices
- Unsubscribe devices
Example requests are provided in tests/example_requests.http. You can use these with:
- VS Code REST Client extension
- Postman
- curl
curl -X POST http://localhost:3000/api/v1/users/user123/subscriptions \
-H "Content-Type: application/json" \
-H "X-UserId: user123" \
-d '{
"subscription": {
"endpoint": "https://fcm.googleapis.com/fcm/send/...",
"keys": {
"p256dh": "...",
"auth": "..."
}
},
"deviceInfo": {
"userAgent": "Mozilla/5.0...",
"deviceType": "desktop",
"browserName": "Chrome"
}
}'curl -X POST http://localhost:3000/internalapi/v1/users/user123/notifications \
-H "Content-Type: application/json" \
-d '{
"notification": {
"title": "Test Notification",
"body": "Hello from the push service!"
}
}'# Run all tests
yarn test
# Run tests in watch mode
yarn test --watch
# Run tests with coverage
yarn test --coverageThe service uses OpenAPI validation. The full API specification is available in openapi.yaml.
POST /api/v1/users/:userId/subscriptions- Subscribe a deviceGET /api/v1/users/:userId/subscriptions- Get user's subscriptionsDELETE /api/v1/users/:userId/subscriptions/:endpoint- Unsubscribe specific deviceDELETE /api/v1/users/:userId/subscriptions- Unsubscribe all devices
POST /internalapi/v1/users/:userId/notifications- Send to specific userPOST /internalapi/v1/notifications/broadcast- Broadcast to all users
The public API implements a simple authorization mechanism:
- All requests must include an
X-UserIdheader - The service validates that the header value matches the
userIdin the URL path - This prevents users from accessing or modifying other users' subscriptions
Important: This service does NOT handle authentication. It expects that:
- Authentication is performed by upstream infrastructure (API Gateway, reverse proxy, auth service, etc.)
- The infrastructure authenticates users and injects the verified
X-UserIdheader - The service trusts this header and only performs authorization checks
Example authorization flow:
- User authenticates with your infrastructure (e.g., JWT token validation)
- Infrastructure verifies the user identity and adds
X-UserId: user123header - Request reaches this service with the trusted header
- Service checks if
X-UserIdmatches the requested resource's userId - Access is granted or denied based on this check
βββββββββββββββββββββββ
β Client App β
β (Your Backend) β
ββββββββββββ¬βββββββββββ
β REST API
β
ββββββββββββΌβββββββββββ βββββββββββββββββββ
β Push Notification ββββββΊβ MongoDB β
β Service β β β
β (This Service) β β - Subscriptions β
β β β β
ββββββββββββ¬βββββββββββ βββββββββββββββββββ
β
β Web Push Protocol
β
ββββββββΌβββββββ
β Push β
β Providers β
β β
β β’ FCM β
β β’ APNS β
β β’ Mozilla β
βββββββββββββββ
-
VAPID Keys: Keep your private key secure. Never commit it to version control.
-
Browser Compatibility:
- Users must grant notification permissions
- Service workers must be served over HTTPS in production
- localhost is allowed for development
-
Subscription Management:
- Invalid subscriptions (410/404 responses) are automatically deleted
- Each device/browser creates a unique subscription per user
-
"Missing VAPID environment variables" error
- Ensure all VAPID keys are set in
.env - Check that keys are properly formatted
- Ensure all VAPID keys are set in
-
MongoDB connection errors
- Verify MongoDB is running
- Check connection string in
.env
-
Notification not received
- Verify browser notification permissions
- Check browser console for service worker errors
- Ensure the page is served over HTTPS (or localhost)
The service uses Pino for structured logging. In development, logs are pretty-printed to the console. In production, logs are output as JSON to stdout.