How To Create Realtime Notifications For HTML5 Apps

Header

There are few apps nowadays for which realtime notifications are not a core feature. From friend requests to pull requests, keeping users updated is key to their experience. Users want to be in the know about the content that interests them, and receive current information without the need for a browser refresh. It improves peer interactions between friends and colleagues, increases social reach, and triggers important conversations that could be invaluable to your app experience.

In this tutorial, we will show you how simple it is to add realtime notifications to your web or mobiles apps with Pusher.

What We’ll Cover

Pusher Terminology

The realtime web is sometimes referred to as “the evented web”. This is because our applications are full of events; events when data changes within your apps, events when users interact with an application and events when systems interact with each other.

Events take place on something: on a table in a database as a new record is created, updated or deleted; on a particular part of the user interface. Anything that results in a change in the underlying application state.

For this reason Pusher uses the terms channel for identifying and partitioning data and events for indicating actions upon that data.

Channels

In Pusher, channels are a way to organize your data. In this tutorial, we will use a channel called notifications, but for other things you might wish to add, such as chat-messages or user-activity, you will want to use new channels to appropriately categorize the data you send.

To transmit notifications, we will be using public channels. Information on public channels is potentially accessible to anybody. To control access to information on channels you would use private channels.

Events

Events are triggered on channels resulting in the event being distributed to all clients that are subscribed to that channel. They have a name, such as new_notification, and a JSON data payload, such as {message: 'hello world'}.

The Tutorial

Now that you’ve grasped channels and events, let’s get started with creating your own realtime notifications app!

Step 0: Setting Up

Sign up for a free account.

Make sure you have a server capable of running Ruby, PHP, Python or Node. With Ruby, we will be running Sinatra; Python with Flask; and Node with Express.

Install the Pusher library for your given language:

    gem install pusher
    pip install pusher==0.8
    composer require pusher/pusher-php-server
    npm install pusher

Create an application, and name it whatever you wish, e.g. ‘Notifications Tutorial’. On the page for your application, you’ll find your application credentials.

Create a directory called notifications with a structure that looks like this:

.
├── app.rb
└── views
    └── index.erb
.
├── app.py
└── templates
    └── index.html
.
├── index.html
└── notification
    └── index.php
// disregarding Express-generated files
.
├── app.js
├── routes
│   ├── index.js
│   └── notification.js
└── views
    └── layout.jade

If at any point you are stuck, feel free to browse the source code (the different languages can be found in different branches).

Step 1: Triggering an event

To start with, we’ll just trigger an event on a channel, and view it in the Pusher debug console.

First off, import the Pusher package or library. And then we initialise our Pusher instance with our app credentials: our app_id, app_key, and app_secret.

Now, for testing purposes we’ll trigger an event called my_event on a channel called my_channel. We’ll give this event an arbitrary payload, such as {message: 'hello world'}.

require 'pusher'

pusher = Pusher::Client.new app_id: 'YOUR APP ID', key: 'YOUR APP KEY', secret: 'YOUR APP SECRET'

# trigger on my_channel' an event called 'my_event' with this payload:

pusher.trigger('my_channel', 'my_event', {
    message: 'hello world'
})
import pusher

p = pusher.Pusher(
  app_id='YOUR APP ID',
  key='YOUR APP KEY',
  secret='YOUR APP SECRET'
)

# trigger on my_channel' an event called 'my_event' with this payload:

p['my_channel'].trigger('my_event', {'message': 'hello world'})
<?php

require(dirname(__FILE__).'/../vendor/autoload.php');

$pusher = new Pusher('YOUR APP KEY', 'YOUR APP SECRET', 'YOUR APP ID');

// trigger on my_channel' an event called 'my_event' with this payload:

$data['message'] = 'hello world';
$pusher->trigger('my_channel', 'my_event', $data);

?>
var Pusher = require('pusher');

var pusher = new Pusher({
   appId: 'YOUR APP ID',
   key: 'YOUR APP KEY',
   secret: 'YOUR APP SECRET'
});

// trigger on my_channel' an event called 'my_event' with this payload:

pusher.trigger('my_channel', 'my_event', {
   message: "hello world"
});

Open up the Pusher debug console for the app you have created. Then execute the code in the file you’ve just edited.

ruby app.rb
python app.py
php notification/index.php
node routes/notification.js

You should see the event pop up in the Pusher debug console. Pretty nifty, huh?

Pusher Debug console image

Step 2: Creating a very basic app

On Your Client

Let’s jump in and see what we do on the client-side when using Pusher.

Include the Pusher JavaScript library in your index.html file. We’ll also include jQuery for rendering notifications in the UI:

<script src="//js.pusher.com/2.2/pusher.min.js"></script>
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>

To get things started, let’s initialize our Pusher instance in a HTML script tag:

<script>
    var pusher = new Pusher('YOUR_APP_KEY');
</script>

Essentially, we wish to bind to an event on a channel, and pass a callback to be called any time the event takes place, such as this:

myChannel.bind('my_event', function(data){
    // do something with our `data`
});

Let’s bind to an event called new_notification on the notifications channel and show it in a <div>:

//subscribe to our notifications channel
var notificationsChannel = pusher.subscribe('notifications');

//do something with our new information
notificationsChannel.bind('new_notification', function(notification){
    // assign the notification's message to a <div></div>
    var message = notification.message;
    $('div.notification').text(message);
});

Now, within your HTML, create a div, with class ‘notification’, such as <div class="notification"></div>. Your HTML should look like this:

<html>
<head>
    <title>Realtime Notifications</title>
    <script src="//js.pusher.com/2.2/pusher.min.js" type="text/javascript"></script>
    <script src="//code.jquery.com/jquery-2.1.3.min.js" type="text/javascript"></script>
</head>
<body>

    <div class="notification"></div>

    <script>

    var pusher = new Pusher('YOUR_APP_KEY');

    var notificationsChannel = pusher.subscribe('notifications');

    notificationsChannel.bind('new_notification', function(notification){
        var message = notification.message;
        $('div.notification').text(message);
    });

    </script>

</body>
</html>

On Your Server

We’ll slightly modify our existing code so that whenever somebody makes a GET request to the /notification endpoint, this code will be executed and a new_notification event will be triggered on the notifications channel.

require 'sinatra' # requires `gem install sinatra`

get '/notification' do
    pusher.trigger('notifications', 'new_notification', {
        message: 'hello world'
    })
    "Notification triggered!"
end

# don't forget to render your index.html page

get '/' do
    erb :index
end
from flask import Flask # requires `pip install flask`
from flask import render_template
from flask import request
app = Flask(__name__)

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

@app.route("/notification")
def trigger_notification():
    p['notifications'].trigger('new_notification', {'message': 'hello world'})
    return "Notification triggered!"

if __name__ == "__main__":
    app.run()
$pusher = new Pusher($app_key, $app_secret, $app_id);

$data['message'] = 'hello world';

$pusher->trigger('notifications', 'new_notification', $data);

// you can run the built-in PHP web server using the following command:
// `php -S localhost:8000`
router.get('/', function(req, res){

    pusher.trigger('notifications', 'new_notification', {
        message: "hello world"
    });

    res.send("Notification triggered!")
});

Run your server and open ‘/’, showing index.html, on one browser window.

Then, open the /notification URL in another browser window and you will see ‘hello world’ appear on the first window.

Step 3: Make It Interactive

On Your Client

That’s all well and good - but what if we want to create notifications that say something other than ‘hello world’? Let’s make it so that we create a notification to all users whenever we submit some text to the UI.

Let’s create our input:

<input class="create-notification" placeholder="Send a notification :)"></input>
<button class="submit-notification">Go!</button>

Now, whenever clicks ‘Go!’, we want to POST to our /notification endpoint with the message to broadcast.

var sendNotification = function(){

    // get the contents of the input
    var text = $('input.create-notification').val();

    // POST to our server
    $.post('/notification', {message: text}).success(function(){
        console.log('Notification sent!');
    });
};

$('button.submit-notification').on('click', sendNotification);

On Your Server

Server-side, we’ll just want to replace our hard-coded ‘hello world’ message with whatever was posted to that endpoint. Furthermore, we’ll want to make sure our code handles a POST request. This is so that we can POST messages to this endpoint to the client to create a notification.

post '/notification' do
    message = params[:message]

    pusher.trigger('notifications', 'new_notification', {
        message: message
    })
end
@app.route("/notification", methods=['POST'])
def trigger_notification():
    message =  request.form['message']
    p['notifications'].trigger('new_notification', {'message': message})
    return "Notification triggered!"
$text = $_POST['message'];

$data['message'] = $text;

$pusher->trigger('notifications', 'new_notification', $data);
router.post('/notification', function(req, res){
    var message = req.param('message');
    pusher.trigger('notifications', 'new_notification', {
        message: message
    });
    res.send("Notification triggered!")
});

Now, open up a second browser to show the index.html file. If you type a piece of text into your input box and click ‘Go!’, you’ll see that all browsers receive your new notification.

Step 4: Improve The Notification Experience

Toastr is a fairly popular and easy-to-use library for nice, non-blocking in-app notifications. In showing you how to further extend your notifications app, let’s use it to display new notifications in the UI.

Link in the Toastr CSS and Javascript in your between your <head> tags in index.html:

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>

Remove our <div class="notification"></div> as we won’t need it any more.

At the time of writing, toastr does not have HTML escaping built in. So at this point, to encourage security and best practices, we should escape our HTML before rendering it to the DOM. Otherwise, our app would be vulnerable to HTML script injection attacks. This should be straightforward; at our notification where we receive new notification POSTs from the client, let’s sanitize the message.

require 'cgi'

post '/notification' do
    message = CGI.escape_html params[:message]

    pusher.trigger('notifications', 'new_notification', {
        message: message
    })
end
import cgi

@app.route("/notification", methods=['POST'])
def trigger_notification():
    message =  cgi.escape(request.form['message'])
    p['notifications'].trigger('new_notification', {'message': message})
    return "Notification triggered!"
$text = $_POST['message'];

$text = htmlspecialchars($_POST['message']);

$data['message'] = $text;
$pusher->trigger('notifications', 'new_notification', $data);
var escapeHTML = require('escape-html'); // needs `npm install escape-html`

router.post('/notification', function(req, res){
    var message = escapeHTML(req.param('message'));
    pusher.trigger('notifications', 'new_notification', {
        message: message
    });
    res.send("Notification triggered!")
});

Now, on the client, let’s simply call toastr.success in our new_notification Pusher callback.

notificationsChannel.bind('new_notification', function(notification){
    var message = notification.message;
    toastr.success(message)
});

So, test it out! You can open up a new browser window to check it works.

What Now?

Try out our (only slightly) prettier demo in multiple browser windows or in your browser and on your phone / tablet.

Have a look at our documentation to other ways in your can integrate Pusher into your application.

To extend what you’ve built, you might want to have a look at the other types of notification Toastr can provide.

Alternatively, you could exclude the sender of the notification from receiving the alert, by using socket_ids to filter recipients.

Here are links to other Javascript notification libraries you can use:

I hope this tutorial was helpful, and don’t be shy to let us know what you’ve built out of it.