Python: how to implement Web Push Notifications in Flask

Python: how to implement Web Push Notifications in Flask

In this article, we will explore how to implement web push notifications using the Python Flask framework.

Web push notifications are a powerful tool to keep the user engaged even when they are not actively on your web application. In this article, we will explore how to implement web push notifications using the Python Flask framework.

Before we get started, you need to have a basic understanding of Python, Flask, and JavaScript. You will also need to have Node.js installed on your system to use some of the necessary tools.

To implement push notifications, we will follow these steps:

  1. Set up the Flask server.
  2. Generate VAPID keys for authentication.
  3. Manage notification subscription in the client.
  4. Send notifications from the Flask server.

First, create a Flask project. Start by installing Flask:


pip install Flask

Then, create an app.py file and set up a simple Flask app:


from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)

Also create a basic HTML template called index.html in the templates folder:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Push Notifications</title>
</head>
<body>
    <h1>Web Push Notifications with Flask</h1>
    <button id="subscribe">Subscribe</button>

    <script src="app.js"></script>
</body>
</html>

Voluntary Application Server Identification (VAPID) keys are used to authenticate the server that sends notifications. You can generate them using an npm package called web-push.

Install web-push:


npm install web-push -g

Generate VAPID keys:


web-push generate-vapid-keys

This command will give you a public key and a private key. Save these keys because we will use them in the Flask server.

In the app.js file, add the following code to handle the client's subscription to push notifications:


const publicVapidKey = 'YOUR_VAPID_PUBLIC_KEY';

if ('serviceWorker' in navigator) {
    send().catch(err => console.error(err));
}

async function send() {
    const register = await navigator.serviceWorker.register('/worker.js', {
        scope: '/'
    });

    const subscription = await register.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
    });

    await fetch('/subscribe', {
        method: 'POST',
        body: JSON.stringify(subscription),
        headers: {
            'content-type': 'application/json'
        }
    });
}

function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding)
        .replace(/-/g, '+')
        .replace(/_/g, '/');

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

Then create a worker.js file in your project's root:


self.addEventListener('push', event => {
    const data = event.data.json();
    self.registration.showNotification(data.title, {
        body: data.message,
        icon: 'icon.png'
    });
});

Install the pywebpush package:


pip install pywebpush

Add the following lines to your app.py to handle subscription and send notifications:


import json
from flask import request
from pywebpush import webpush, WebPushException

VAPID_PUBLIC_KEY = "PUBLIC_VAPID_KEY"
VAPID_PRIVATE_KEY = "PRIVATE_VAPID_KEY"

@app.route('/subscribe', methods=['POST'])
def subscribe():
    subscription_info = request.get_json()
    return "Subscribed", 201

def send_web_push(subscription_information, message_body):
    try:
        webpush(
            subscription_info=subscription_information,
            data=json.dumps(message_body),
            vapid_private_key=VAPID_PRIVATE_KEY,
            vapid_claims={
                "sub": "mailto:you@email.com"
            }
        )
    except WebPushException as ex:
        print(f"Error: {repr(ex)}")

@app.route('/notify', methods=['POST'])
def notify():
    subscription_info = request.get_json()
    message = {
        "title": "Title",
        "message": "Body"
    }
    send_web_push(subscription_info, message)
    return "Notification sent", 200

Now, you can test notifications by running the Flask app and sending a POST request to /notify with the subscription data.

Conclusion

With these steps, you have implemented a simple web push notification system using Flask. This example can be extended and customized to meet the specific needs of your application. Push notifications are a powerful tool for keeping users engaged, and with Flask, implementing them is quite straightforward.