Posted on 51 Comments

Web Push Using Laravel 8 Notifications *Updated*

Web Push Is Awesome

No seriously! It’s a pivotal moment in web development. You see web push is a W3C protocol that allows websites to communicate with a user’s browser in the background, using this web developers can now do things such as: background push notifications, offline sync, and background analytics just to name few. The web push api follows the protocol and consist of 4 main stages:

  1. User Agent installs service worker
  2. App asks permission to show notifications
  3. Once permission is granted, subscribe to push
  4. Save subscription details on backend
  5. Send notification via Laravel notification

All the code can be seen on my Github and the live demo can be seen here!

Components Of Web Push

Implementing web push requires a true full-stack approach, on the back-end we have to:

  • Implement the WebPush notification channel
  • Set up VAPID keys (more details later)
  • Configure the user model to manage subscriptions
  • Fire the notifications

On the front end we must:

  • Create and install a service worker, the majority of our logic will be contained here and is responsible to handling push notifications
  • Give user prompt to accept permissions
  • Give user ability to send notification to self

I will break it down into back-end and front-end implementations as to not confuse you; let’s start with the back-end.
 

Creating The Backend

Create a blank Laravel application and run the following composer command to download the web push notification channel.

composer require laravel-notification-channels/webpush

The User Model

Next open up your user model and add the following.

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use NotificationChannels\WebPush\HasPushSubscriptions;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasPushSubscriptions;

/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'name',
'email',
'password',
];

/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

This added HasPushSubscriptions trait allows the user model to receive push notifications. Without this trait the application won’t know what model to store the tokens on.


Next publish the migration with:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="migrations"

Run the migrate command to create the necessary tables:

php artisan migrate

You can also publish the config file with the following command. We won’t be doing any customizations in part 1 but maybe down the line:

php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="config"

Generate the VAPID keys with (required for browser authentication) with the following artisan command. This command will set VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEYin your .env file. VAPID is a web push protocol that is needed if we want to send push notifications. Basically it voluntarily identifies itself to a push notification server. If you want to read the specification you can here:

php artisan webpush:vapid

The Notification Class

Next let’s create the notification being used. For now we will make a generic catch all notification. In later tutorials we may add some more custom notifications:

php artisan make:notification GenericNotification

Open up the file and replace with the following contents

<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use NotificationChannels\WebPush\WebPushMessage;
use NotificationChannels\WebPush\WebPushChannel;
class GenericNotification extends Notification
{
    use Queueable;
    public $title, $body;
    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($title, $body)
    {
        //
        $this->title = $title;
        $this->body = $body;
    }
    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return [WebPushChannel::class];
    }
    public function toWebPush($notifiable, $notification)
    {
      $time = \Carbon\Carbon::now();
        return WebPushMessage::create()
            // ->id($notification->id)
            ->title($this->title)
            ->icon(url('/push.png'))
            ->body($this->body);
            //->action('View account', 'view_account');
    }
}

Let’s break down this class. First off in the constructor() method we set the title and the body of the notification. This will be used to show the title and body of the push notification on the browser. The via() method we want to set the channel to the WebPushChannel. This should be self explanatory as we want to deliver via push notifications.

The API Routes

Next open up routes/api.php and fill out the API routes

<?php
use Illuminate\Http\Request;
use App\Models\User; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::middleware('auth:api')->get('/user', function (Request $request) { return $request->user(); }); Route::post('/save-subscription/{id}',function($id, Request $request){ $user = \App\Model\User::findOrFail($id); $user->updatePushSubscription($request->input('endpoint'), $request->input('keys.p256dh'), $request->input('keys.auth')); $user->notify(new \App\Notifications\GenericNotification("Welcome To WebPush", "You will now get all of our push notifications")); return response()->json([ 'success' => true ]); }); Route::post('/send-notification/{id}', function($id, Request $request){ $user = \App\Model\User::findOrFail($id); $user->notify(new \App\Notifications\GenericNotification($request->title, $request->body)); return response()->json([ 'success' => true ]); });

As you can see, we only added two additional routes to the application, both of which are POST. The first route

/save-subscription/{id}

Is responsible for saving the web push subscription from the client. Once the client requests web push access a set of keys are generated, which must be sent to our server. Using the updatePushSubscription() method that comes with the trait added to the user model, we can set the webpush subscription for whatever url the user is requesting from. The updatePushSubscription method takes 3 parameters:

  • endpoint (required): This is the created on the front end when registering for push notifications
  • key (optional) : Needed to encrypt data, only encrypted messages can have a payload
  • token (optional): Needed to encrypt data, only encrypted messages can have a payload

That’s it for the back end! In the next tutorial we will make a Vue.js application and register for push notifications and test it out! If you enjoyed my content please like/subscribe/share! If you want another cool project learn how to stream your desktop to a recording or to YouTube!