Ruby On Rails (6.0 +) on web-packer| Send Push notifications with WebPush

Hamzawais
5 min readJun 2, 2022

--

In this short article, we will discuss how we can integrate push notifications in your Ruby On Rails project. Most of the resources do not tackle the latest versions of Rail, and a new developer can get confused with project directories and file placements.

No worries, Hamza is here. Let’s dive straight into it.

Pre-requisites:

  1. You have a Rails (version ≥ 6.0) installed.
  2. Webpacker is configured properly.
  3. StimulusJS for javascript (optional)
  4. Devise with User model setup.

Step 1: Let’s get the gem

You can install the gem by opening your Gemfile, and adding:

Gemfile
gem “webpush”

run bundle install

After running this, your project will have webpush gem installed.

Step 2: Secrets ;)

For those of you who are new to development, we have secret keys for third party services. They can be in form of API keys, devise secrets etc. For webpush, we will have to generate our keys. We will generate VAPID keys for our project. These can be generated through your rails console

rails console
vapid_key = Webpush.generate_key
vapid_key.public_key
vapid_key.private_key

Step 2.1: Placing them vapid keys

There are various places you can store these keys. For example, AWS param store, environment variables, or in your .key files. Store wherever you want, but do not add them in line. PLEASE. PLEASE.

We now have keys, specific to this project and environment. If you might want to generate more keys if you have a production server or a testing/staging server. Ok, So far so good.

Step 3.0 Creating a channel of communication b/w the server and the client.

This may get confusing but stay with me.

Step 3.1: Stimulus controller

Let’s create a stimulus controller called ‘main’. I’m using StimulusJS, but it’s optional. You can write plain JS if you want, but make sure it’s accessible on every page.

I’ll explain what’s happeing here in a minute

Here’s a gist

var vapidPublicKey = new Uint8Array(this.vapValue);

Getting the public vapid key. I’ll explain how we get the key sent to stimulus in Step 3.3.

navigator.serviceWorker.register('/serviceworker.js')

We register the service worker here. We will be creating a service worker in the next step (3.2)

$.ajax({                
url: '/register_subscription',
method: 'patch',
data: { 'subscription': JSON.stringify(subscription) } });

We are attempting to store a new subscription for the user. We’ll discuss it later in the article.

Step 3.2: Adding and registering Service worker

Create a new file serviceworker.js under public/ folder. Here’s a gist for what you are going to add.

Basically, as soon as the browser gets the notification from your server, the service worker will intercept it and show a push notification on the browser.

mp.png is a dummy logo I have under public/ directory. You can change it if you want

Step 3.3 Sending Public VAPID key to our client / Stimulus controller

Go to application.html.erb, and add this

<body data-controller='app' data-app-vap-value='<%= Base64.urlsafe_decode64(Rails.application.credentials[Rails.env.to_sym].dig(:vapid, :public_key)).bytes %>'>
--------
</body>

We are fetching our Vapid Public key from our secret manager, and sending it to our stimulus controller.

The reason is that no matter which page is your user on, our registration code will work regardless. It will ask the user if they want to accept notifications or not. If they accept it, we will register that subscription in our database, so that we can send them notifications later using the same subscription.

Step 4: Storing registration in our Database

Remember this?

$.ajax({                
url: '/register_subscription',
method: 'patch',
data: { 'subscription': JSON.stringify(subscription) } });

This is our client trying to register the subscription. Let’s build a route quickly

Go to routes.rb

resource :users, only: [:edit] do  collection do    patch :register_subscription  endend

create a model called PushSubscription.

The migration will look like this:

class CreatePushSubscriptions < ActiveRecord::Migration[6.0]  def change    create_table :push_subscriptions do |t|      t.string :endpoint      t.string :auth_key      t.string :p256dh_key      t.belongs_to :user      t.timestamps    end  endend

app/models/user.rb

has_many :push_subscriptions, dependent: :destroy

endpoint, auth_key, p256dh_key are sent to us when a client subscibes to notifications. We will be storing them in our database

app/controllers/users_controler.rb

This is the logic I have to register the subscription:

def register_subscription  subscription_params = JSON.parse params[:subscription]  subscription =     current_user.push_subscriptions.find_or_initialize_by(  endpoint: subscription_params["endpoint"],  auth_key: subscription_params['keys']['auth'],  p256dh_key: subscription_params['keys']['p256dh'],)  subscription.saveend

In this step we focused on our business logic to create and store subscription. A subscription is created against a user and their device.

Step 5: Permissions

We need to request the user permission if they haven’t allowed our site to send notifications for them. Just paste the code below:

app/javascript/packs/application.js

if (!("Notification" in window)) {  console.error("This browser does not support desktop   notification");}// Let's check whether notification permissions have already been grantedelse if (Notification.permission === "granted") {  console.log("Permission to receive notifications has been granted");}// Otherwise, we need to ask the user for permissionelse if (Notification.permission !== 'denied') {  Notification.requestPermission(function (permission) {  // If the user accepts, let's create a notification    if (permission === "granted") {    console.log("Permission to receive notifications has been       granted");  }});}else {}

This will take care of the permissions in your browser.

Step 6: SENDING THE NOTIFICATION

You can have different criteria of sending out the notification. But for the sake of simplicity we will add a method to our user model, which will push notification to the client/user’s browser

Lets head back to our user model

And BOOMMMM! You should start receiving notification whenever you call this method.

Troubleshoot

  1. You may want to re-start your server after making changes, because serviceworker gets cached.
  2. You may want to close your browser and restart it. Happened to me.
  3. You may want to make sure your computer IS NOT ON DO NOT DISTURB mode. You’ll end up scratching your head.
  4. You may want to make sure your browser has permissions enabled for notification. Go to notification center and just check if you are on MACOS.

I hope you enjoyed going through the article. Please feel free to reach out to me if you have any questions, or facing issues. Do leave a comment if I helped you.

Best,

Hamza

You get in touch with me:

email: hamzawais54@gmail.com | Phone: +923244105651 | web: meet-hamza.com | linkedin: https://www.linkedin.com/in/hamza-awais-1908/

--

--