Skip to content

Repository files navigation

WebPush-dispatcher

Node server application to simplify sending bulk Web Push Messages. Submit hundreds of web push messages in a single HTTP request. The server will dispatch those messages asynchronously and concurrently.

For an Apple Push (APNs) dispatcher with the same asynchronous webhook pattern, see APPLE_DISPATCHER_CONTRACT.md. For a Firebase Cloud Messaging (FCM) dispatcher with the same asynchronous webhook pattern, see FIREBASE_DISPATCHER_CONTRACT.md.

Tracking messages using webhooks

You likely want to know keep track if a push subscription had expired or not in order to keep your subscriber database up-to-date. If you supply a webhook url to a message a POST will be sent to it holding the submission response.

Concurrency

There is no limit on the number of simultaneous requests to the push endpoints. This ensures the messages will be sent as quickly as possible.

The webhook calls are limited to 20 concurrent requests per origin. This number can be configured.

Configuration

Create .env file to set the environment variables:

HTTP_PORT=8080
WEBPUSH_SUBJECT=https://example.com
WEBPUSH_PUBLIC_KEY=<PUBLIC KEY>
WEBPUSH_PRIVATE_KEY=<PRIVATE KEY>
MAX_WEBHOOK_SOCKETS=20
APNS_TEAM_ID=<APPLE TEAM ID>
APNS_KEY_ID=<APPLE KEY ID>
APNS_TOPIC=<IOS BUNDLE ID>
APNS_AUTH_KEY_PATH=/run/secrets/AuthKey_ABC123DEF4.p8
APNS_ENVIRONMENT=production
APNS_MAX_CONCURRENCY=100
APNS_REQUEST_TIMEOUT_MS=10000
APNS_RETRY_MAX=5
APNS_RETRY_BASE_MS=300
FCM_PROJECT_ID=<GCP PROJECT ID>
FCM_CREDENTIALS_PATH=/run/secrets/firebase-service-account.json
FCM_MAX_CONCURRENCY=100
FCM_REQUEST_TIMEOUT_MS=10000
FCM_RETRY_MAX=5
FCM_RETRY_BASE_MS=300

Running using NodeJS

Install NodeJS and NPM, then start the server as follows:

$ npm install
$ export $(cat .env | xargs) && node server.js

Running from Docker

Check out the repository, edit docker-compose.yml to your taste and run the following command:

$ docker-compose up

The above command keeps the container in the foreground so you can stop it again using Ctrl-C.

To create a container and run it in the background:

$ docker-compose up -d

Configuration

The following environment variables can be set:

name description default
HTTP_PORT The TCP port the web server listens to 8080
WEBPUSH_SUBJECT the subject used to create the VAPID keypair for none.
WEBPUSH_PUBLIC_KEY the VAPID public key none
WEBPUSH_PRIVATE_KEY the VAPID private key none
MAX_WEBHOOK_SOCKETS maximum concurrent connection per origin for webhook calls 20
APNS_TEAM_ID Apple developer team id none
APNS_KEY_ID Apple APNs key id none
APNS_TOPIC iOS bundle id used as APNs topic none
APNS_AUTH_KEY_PATH filesystem path to Apple .p8 key file none
APNS_ENVIRONMENT APNs environment (production or sandbox) production
APNS_MAX_CONCURRENCY max concurrent APNs sends 100
APNS_REQUEST_TIMEOUT_MS APNs request timeout in ms 10000
APNS_RETRY_MAX max retry attempts for transient APNs failures 5
APNS_RETRY_BASE_MS base retry delay in ms 300
FCM_PROJECT_ID GCP project id used for FCM HTTP v1 endpoint none
FCM_CREDENTIALS_PATH filesystem path to Firebase service account JSON none
FCM_CREDENTIALS_JSON inline Firebase service account JSON (alternative to path) none
FCM_MAX_CONCURRENCY max concurrent FCM sends 100
FCM_REQUEST_TIMEOUT_MS FCM request timeout in ms 10000
FCM_RETRY_MAX max retry attempts for transient FCM failures 5
FCM_RETRY_BASE_MS base retry delay in ms 300

Sending messages

To send messages, send a JSON body in a POST request to the server.

Web push endpoints:

  • POST /
  • POST /dispatch
  • POST /dispatch/web

Apple push endpoint:

  • POST /dispatch/apple

Firebase push endpoint:

  • POST /dispatch/firebase

See APPLE_DISPATCHER_CONTRACT.md for the Apple message and webhook schema. See FIREBASE_DISPATCHER_CONTRACT.md for the Firebase message and webhook schema.

Here is a minimal example for sending a single push message:

{
  "messages": [
    {
      "subscription" : {
        "endpoint" : "subscription_endpoint_url",
        "keys" : {
          "auth" : "auth_token",
          "p256dh" : "auth_publickey"
        }
      },
      "payload": "message payload"
    }
  ]
}

Here is a full example with multiple messages and webhook urls:

{
  "messages": [
    {
      "subscription" : {
        "endpoint" : "subscription_endpoint_url",
        "keys" : {
          "auth" : "auth_token",
          "p256dh" : "auth_publickey"
        }
      },
      "payload": "message payload",
      "webhook": "https://www.example.com/push-webhook/?message=1"
    },
    {
      "subscription" : {
        "endpoint" : "subscription_endpoint_url",
        "keys" : {
          "auth" : "auth_token",
          "p256dh" : "auth_publickey"
        }
      },
      "payload": "message payload",
      "webhook": "https://www.example.com/push-webhook/?message=2"
    }
  ]
}

Processing WebHook calls

If a webhook url is set for a message, the web push result will be sent as a JSON POST request.

Usually, submitting a web push will result in a 201 response like this:

{
	"statusCode": 201,
	"body": "",
	"headers": {
		"content-length": "0",
		"location": "https://wns2-am3p.notify.windows.com/messageId/4901058...",
		"x-wns-notificationstatus": "received",
		"x-wns-status": "received",
		"x-wns-msg-id": "44040BAAD807E576",
		"strict-transport-security": "max-age=31536000; includeSubDomains",
		"date": "Wed, 26 Jul 2023 18:21:46 GMT"
	}
}

If a web push subscription has expired, the web push will result in a 410 (Gone) response:

{
	"name": "WebPushError",
	"message": "Received unexpected response code",
	"statusCode": 410,
	"headers": {
		"content-type": "text/plain; charset=utf-8",
		"x-content-type-options": "nosniff",
		"x-frame-options": "SAMEORIGIN",
		"x-xss-protection": "0",
		"date": "Wed, 26 Jul 2023 18:26:36 GMT",
		"content-length": "47",
		"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
	},
	"body": "push subscription has unsubscribed or expired.\n",
	"endpoint": "https://fcm.googleapis.com/fcm/send/esbX9MtCVko..."
}

About

Web Push Dispatcher with support for APN / FCM notifications

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages