Send push notifications to your browser from Adonis.js

  • Ethiel Adiassa
December 6th, 2018
You will need Node installed on your machine.

Introduction

In this tutorial, I will show how to send push notifications to your browser from your backend. We’ll use Adonis.js as our Node.js framework, and we’ll push notifications from our Adonis app to our browser through Pusher channels.

Web push notifications are an instant means of direct communication between a website and its customers. They appear as clickable messages on web browsers in desktop and Android devices. This is the easiest way to gather more subscribers without collecting email IDs and contact details.

Demo

Here is the final result of our app:

Prerequisites

In order to follow this tutorial, knowledge of JavaScript and Node.js, more precisely Adonis.js is required. You should also have the following installed on your machine:

Set up your Adonis project

Open your terminal and type this command to install Adonis CLI:

        # if you don't have Adonis CLI installed on your machine. 
        $ npm install -g @adonisjs/cli

        # Create a new adonis app and move into the app directory
        $ adonis new adonis-push-notifications && cd adonis-push-notifications

Start the server and test if it's working:

        $ adonis serve --dev
        2018-10-18T09:09:16.649Z - info: serving app on http://127.0.0.1:3333

Edit the welcome view

Go to the resources/views directory and replace the content of welcome.edge file with:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8"/>
      <title>Hello Adonis</title>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
      <link rel="stylesheet"
            href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.min.css">
      {{ style('style') }}
    </head>
    <body>

    <div id="app" class="container" style="margin-top: 160px">
      <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-8">
          <div class="card">
            <div class="card-header">Broadcast a Notification message</div>
            <div class="card-body">
              <div class="container">
                <div class="row justify-content-md-center">
                  <div class="col col-md-10">
                    @if(old('status'))
                    <div class="alert alert-success" role="alert">
                      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                        <span aria-hidden="true">×</span>
                      </button>
                      {{ old('status') }}
                    </div>
                    @endif
                    <form method="POST" action="{{ route('sendNotification') }}">
                      {{ csrfField() }}
                      <div class="form-group row">
                        <label class="col-md-3 col-form-label">

                          Notification Message
                        </label>
                        <div class="col-md-9">
                          <input type="text" name="message" placeholder="Send Notification"
                                 autocomplete="off"
                                 class="form-control" required>
                        </div>
                      </div>
                      <div class="form-group row">
                        <div class="col-md-3"></div>
                        <div class="col-md-6">
                          <button type="submit" class="btn btn-primary btn-block">
                            <i class="fa fa-btn fa-paper-plane"></i> Send Notification
                          </button>
                        </div>
                      </div>
                    </form>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    {{ script('https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js') }}
    {{ script('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/js/bootstrap.min.js') }}

    </body>
    </html>

As you can see, we are importing the CSS files using the css method. We do a similar thing with JavaScript, we use script method to import .js. Flash messages are used to display incoming messages from the server. Flash messages are stored temporarily in sessions by the server to display as browser notifications.

Refresh your browser:

Install the Pusher SDK

Go to Pusher and create an account or sign in if you already have an account.

Next, create a new Pusher app instance. This registration provides credentials which can be used to communicate with the created Pusher instance. Copy the App ID, Key, Secret, and Cluster from the App Keys section and put them in the .env file located at you project root:

    //.env
        PUSHER_APP_KEY=<APP_KEY>
        PUSHER_APP_SECRET=<APP_SECRET>
        PUSHER_APP_ID=<APP_ID>
        PUSHER_APP_CLUSTER=<APP_CLUSTER>

We’ll use these keys further in this tutorial to link Pusher with our Adonis project.

Next, we need to install the Pusher SDK as well as other dependencies we need to build our app. We won’t use the Pusher SDK directly but instead use a Pusher provider for Adonis. This provider enables us to use the Pusher SDK easily with the Adonis.js ecosystem. We should first install the Pusher SDK by running this command:

    #if you want to use npm
    npm install pusher

    #or if you prefer Yarn
    yarn add pusher

Now, you can install the Pusher provider for Adonis with this command:

    #if you want to use npm
    npm install adonis-pusher

    #or if you prefer Yarn
    yarn add adonis-pusher

You will need to add the provider to AdonisJS at start/app.js:

    const providers = [
        ...
        'adonis-pusher/providers/Pusher'
    ]

Define routes and your controller

In order to make requests to our backend, we need to define our routes and a controller responsible for the logic our app should have.

Type this command in your terminal to create your controller: adonis:make Notification . Open the routes.js file located at ../start/routes.js and replace the existing code with the following:

    const Route = use('Route')
    const Event = use('Event')

    Route.on('/').render('welcome')

    Route.post('/sendNotification','NotificationController.sendNotification').as('sendNotification')

The first two lines pull in the Route and Event providers for us. Next we tell our app to render the welcome.edge view to the / get request. And the last route accepts a message from the client and defines the sendNotification function (which we’ll create very soon) for handling the logic.

Now, let’s create our sendNotification function we talked about earlier. Paste this piece of code inside your NotificationController file:

    //../app/Controllers/Http/NotificationController.js
    'use strict'

    const Event = use('Event');

    class NotificationController {

      async sendNotification({request, session, response}) {

        let message = request.input('message');

        Event.emit('send::notification', message)

        session.flash({status: 'Notification sent'})
        return response.redirect('back')

      }
    }

    module.exports = NotificationController

We first pull in the Event service provider, then we accept a message from the client post request.

Last we emit an event named send::notification and redirect the user back with a flash message.

Connecting Adonis and Pusher

Create a filename event.js in the start directory. In this file we’ll create an event which will be fired every time we need to send a message via Pusher Channels, and as it happens a notification via Pusher Channels.

    //../start/events.js
    const Event = use('Event')
    const Env = use('Env')
    const Pusher = use('Pusher')

    let pusher = new Pusher({
      appId: Env.get('PUSHER_APP_ID'),
      key: Env.get('PUSHER_APP_KEY'),
      secret: Env.get('PUSHER_APP_SECRET'),
      cluster: Env.get('PUSHER_APP_CLUSTER'),
      encrypted: false
    })

    //fire new event
    Event.when('send::notification', async (message) => {
      pusher.trigger('notification-channel', 'new-notification', {
        message
      })
    })

We need to pull in the Event, Pusher (using the adonis-pusher package we installed earlier) and Env service providers. Next, we registered a listener for the send::notification event, after which we initialized and configure Pusher. This event was registered in the sendNotification function we created above to handle notification post. When we are done with the pusher configuration, we trigger a new-notification event on the notification-channel with the trigger method.

Subscribing to Pusher Events and send notifications

The client needs to start listening to these events being emitted by Pusher. Go to the resources/views directory and update your welcome.edge by adding the following code:

    <!-- Include the Pusher Client library -->
    {{ script('https://js.pusher.com/4.3/pusher.min.js') }}
    <script>

      //Open a connection to Pusher
      let notify = ({message}) => {

        if (!("Notification" in window)) {
          alert("This browser does not support desktop notification");
        }

        Notification.requestPermission().then(function (result) {
          if (result === 'granted') {
            let notification = new Notification(message)
          }
        });
      };


      let pusher = new Pusher(YOUR_PUSHER_APP_ID, {
        cluster: YOUR_PUSHER_CLUSTER,
        encrypted: false
      });

      //Subscribe to the channel we specified in our Adonis Application
      let channel = pusher.subscribe('notification-channel');

      //Listen for events on the channel
      channel.bind('new-notification', (data) => {
        notify(data)
      })


    </script>

First, we include the Pusher client library in order to use Pusher instance from our browser. As you can see we also create a notify function. This function takes the message we intend to send as a push notification in parameter, checks if browser push notifications are supported by the browser and display a new notification to us.

Next, we initialize the Pusher service by passing in our App Key (replace with your actual keys), and some other options (cluster, encrypted). The initialized instance is used to subscribe to the notification-channel channel. Finally, we listened to the new-notification event and send notifications with the notify function we created earlier.

Conclusion

In summary, we learned how we can use Pusher channels to send notifications from an Adonis.js backend. Events sent by Pusher channels are listened on the client side and displayed as a push notifications using browser notifications web API. You can find the full source code on GitHub here.

  • Channels

© 2018 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered office is at 160 Old Street, London, EC1V 9BW.