Push API guide

Even if the web application is not currently opened in the browser or is not running on the device, Push API still allows the web application to receive messages pushed by the server.

Even if the web application is not currently opened in the browser or is not running on the device, the Push API still allows the web application to receive messages pushed by the server.

Using Push API, even if the user is not browsing the site, he can send a message to the user and push the message from the server to the client.

This allows you to send notifications and content updates, enabling you to reach a larger audience.

This is huge because one of the missing pillars of mobile networks compared to native apps is the ability to receive notifications and offline support.

Is there good support?

Push API is the latest feature of the browser API, Chrome (desktop and mobile devices), Firefox and Opera have been supported by Edge since 2016, and Edge has been supported since version 17 (early 2018). For more information, please visit:https://caniuse.com/#feat=push-api

IE does not support it, andSafari has its own implementation.

Since Chrome and Firefox support this feature, approximately 60% of users can access it when browsing on a desktop computer, sosafeuse.

How it works

Overview

When a user accesses your web application, you can trigger a panel to ask whether to allow sending of updates. A kindservice personnelHas been installed and operated in the background to monitorPush event.

Push and notification are a separate concept and API, sometimes due toPush notificationTerm used in iOS. Basically, when a push event is received using the Push API, the Notifications API will be called.

yourserverSend the notification to the client, if permission is obtained, the service worker will receivePush event. The service staff reacted to this incident in the following waysTrigger notification.

Obtain user's permission

The first step in using Push API is to obtain the user's permission to receive data from you.

Many sites have poor results when implementing this panel, which is displayed when the first page loads. The user is not yet convinced that your content is good, and they will deny the permission. Do it wisely.

There are 6 steps:

  1. Check if the service worker is supported
  2. Check if Push API is supported
  3. Registered service staff
  4. Request permission from user
  5. Subscribe to users and get PushSubscription objects
  6. Send PushSubscription object to your server

Check if the service worker is supported

if (!('serviceWorker' in navigator)) {
  // Service Workers are not supported. Return
  return
}

Check if Push API is supported

if (!('PushManager' in window)) {
  // The Push API is not supported. Return
  return
}

Registered service staff

This code is registered atworker.jsThe files are placed in the domain root directory:

window.addEventListener('load', () => {
  navigator.serviceWorker.register('/worker.js')
  .then((registration) => {
    console.log('Service Worker registration completed with scope: ',
      registration.scope)
  }, (err) => {
    console.log('Service Worker registration failed', err)
  })
})

To learn more about how the service staff work in detail, please seeService staff guide.

Request permission from user

Now that the service worker is registered, you can request permission.

The API to perform this operation changes over time, from accepting a callback function as a parameter to returningcommitted to, Breaking backward and forward compatibility, we need to doBothBecause we don't know which method the user's browser implements.

The code is as follows, callingNotification.requestPermission().

const askPermission = () => {
  return new Promise((resolve, reject) => {
    const permissionResult = Notification.requestPermission((result) => {
      resolve(result)
    })
    if (permissionResult) {
      permissionResult.then(resolve, reject)
    }
  })
  .then((permissionResult) => {
    if (permissionResult !== 'granted') {
      throw new Error('Permission denied')
    }
  })
}

ThispermissionResultvalue is a string, which can have the following values:-granted-default-denied

This code causes the browser to display the permissions dialog:

The browser permission dialogue

If the user clicks "Block", you will no longer be able to request permission from that user, Unless they manually enter and unblock the website in the browser’s advanced settings panel (very unlikely).

Subscribe to users and get PushSubscription objects

If the user grants us a license, we can subscribe to that license and callregistration.pushManager.subscribe().

const APP_SERVER_KEY = 'XXX'

window.addEventListener(‘load’, () => { navigator.serviceWorker.register(’/worker.js’) .then((registration) => { askPermission().then(() => { const options = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY) } return registration.pushManager.subscribe(options) }).then((pushSubscription) => { // we got the pushSubscription object } }, (err) => { console.log(‘Service Worker registration failed’, err) }) })

APP_SERVER_KEYIs a string-calledApplication server keyorVAPID key-Identifies the application public key, part of the public/private key pair.

For security reasons, it will be used as part of the verification to ensure that you (only you, not others) can send push messages back to the user.

Send PushSubscription object to your server

In the previous section, we obtainedpushSubscriptionObject, which contains all the content we need to send push messages to users. We need to send this information to our server so that we can send notifications later.

We first create the JSON representation of the object

const subscription = JSON.stringify(pushSubscription)

We can useExtract API:

const sendToServer = (subscription) => {
  return fetch('/api/subscription', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(subscription)
  })
  .then((res) => {
    if (!res.ok) {
      throw new Error('An error occurred')
    }
    return res.json()
  })
  .then((resData) => {
    if (!(resData.data && resData.data.success)) {
      throw new Error('An error occurred')
    }
  })
}

sendToServer(subscription)

Service-Terminal/api/subscriptionThe endpoint receives the POST request and can store the subscription information in its storage.

How the server side works

So far, we have only discussed the client part: the permission to get user notifications in the future.

What about the server? What should it do and how should it interact with customers?

These server-side examples use Express.js (http://expressjs.com/) As a basic HTTP framework, but you can write server-side Push API handlers in any language or framework

Register a new customer subscription

When the client sends a new subscription, please remember that we used/api/subscriptionHTTP POST endpoint, send the details of the PushSubscription object in JSON format in the body.

We initialize Express.js:

const express = require('express')
const app = express()

This utility function ensures that the request is valid, with body and endpoint attributes, otherwise it will return an error to the client:

const isValidSaveRequest = (req, res) => {
  if (!req.body || !req.body.endpoint) {
    res.status(400)
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({
      error: {
        id: 'no-endpoint',
        message: 'Subscription must have an endpoint'
      }
    }))
    return false
  }
  return true
}

The next utility function saves the reservation to the database and returns the resolved promise when the insertion is completed (or failed). ThisinsertToDatabaseFunction is a placeholder, we won't go into details here:

const saveSubscriptionToDatabase = (subscription) => {
  return new Promise((resolve, reject) => {
    insertToDatabase(subscription, (err, id) => {
      if (err) {
        reject(err)
        return
      }
  <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">id</span>)
})

}) }

We use these functions in the POST request handler below. We check if the request is valid, then save the request, and then returndata.success: trueReply to the client, or an error occurs:

app.post('/api/subscription', (req, res) => {
  if (!isValidSaveRequest(req, res)) {
    return
  }

saveSubscriptionToDatabase(req, res.body) .then((subscriptionId) => { res.setHeader(‘Content-Type’, ‘application/json’) res.send(JSON.stringify({ data: { success: true } })) }) .catch((err) => { res.status(500) res.setHeader(‘Content-Type’, ‘application/json’) res.send(JSON.stringify({ error: { id: ‘unable-to-save-subscription’, message: ‘Subscription received but failed to save it’ } })) }) })

app.listen(3000, () => { console.log(‘App listening on port 3000’) })

Send push message

Now that the server has registered the client in its list, we can send Push messages to it. Let's see how it works by creating a sample code snippet that will extract all subscriptions and send a Push message to all subscriptions at the same time.

We use the library becauseWeb Push ProtocolYescomplicated, And lib allows us to abstract a lot of low-level code to ensure that we can work safely and properly handle any edge cases.

This example usesweb-push Node.jslibrary(https://github.com/web-push-libs/web-push) Processing and sending Push messages

We first initializeweb-pushlib, we generate a tuple of private and public keys and set them as VAPID details:

const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()

const PUBLIC_KEY = ‘XXX’ const PRIVATE_KEY = ‘YYY’

const vapidKeys = { publicKey: PUBLIC_KEY, privateKey: PRIVATE_KEY }

webpush.setVapidDetails( mailto:[email protected], vapidKeys.publicKey, vapidKeys.privateKey )

Then we build atriggerPush()Method, responsible for sending the push event to the client. It's just callingwebpush.sendNotification()And catch any errors. If the HTTP status code that returns an error is410, meaning isGone, We will delete the subscriber from the database.

const triggerPush = (subscription, dataToSend) => {
  return webpush.sendNotification(subscription, dataToSend)
  .catch((err) => {
    if (err.statusCode === 410) {
      return deleteSubscriptionFromDatabase(subscription._id)
    } else {
      console.log('Subscription is no longer valid: ', err)
    }
  })
}

We did not implement getting the subscription from the database, but kept it as a stub:

const getSubscriptionsFromDatabase = () => {
  //stub
}

The essence of the code is that the POST request/api/pushEndpoint:

app.post('/api/push', (req, res) => {
  return getSubscriptionsFromDatabase()
  .then((subscriptions) => {
    let promiseChain = Promise.resolve()
    for (let i = 0; i < subscriptions.length; i++) {
      const subscription = subscriptions[i]
      promiseChain = promiseChain.then(() => {
        return triggerPush(subscription, dataToSend)
      })
    }
    return promiseChain
  })
  .then(() => {
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({ data: { success: true } }))
  })
  .catch((err) => {
    res.status(500)
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({
      error: {
        id: 'unable-to-send-messages',
        message: `Failed to send the push ${err.message}`
      }
    }))
  })
})

What the above code does is: it gets all subscriptions from the database, then iterates over them, and then callstriggerPush()The function we explained before.

After the subscription is completed, we will return a successful JSON response, unless an error occurs and a 500 error is returned.

In the real world...

Unless you have a very specific use case, or you just want to learn technology or like DIY, it is unlikely to set up your own Push server. Instead, you usually want to use OneSignal (https://onesignal.com), transparently and freely handle Push events on all platforms (including Safari and iOS).

Receive push events

When a Push event is sent from the server, how does the client get it?

This is normalJavaScriptEvent listener, inpushEvent, which runs inside the Service Worker:

self.addEventListener('push', (event) => {
  // data is available in event.data
})

event.datacontainPushMessageDataObject, which exposes methods for retrieving the push data sent by the server in the required format:

You would usually useevent.data.json().

Show notification

Here we are withNotification API, But this is for good reason, because one of the main use cases of Push API is to display notifications.

In uspushIn the event listener in the Service Worker, we need to display a notification to the user and tell the event to wait until the browser displays the event before the function can terminate. We extend the survival time of the incident until the browser finishes displaying the notification (until the fulfillment of the promise is resolved), otherwise the service worker may stop during processing:

self.addEventListener('push', (event) => {
  const promiseChain = self.registration.showNotification('Hey!')
  event.waitUntil(promiseChain)
})

More information about notificationsNotification API guide.

Download mine for freeJavaScript beginner's manual


More browser tutorials: