Broadcasting Laravel notifications feat. JWT, React, Laravel Echo and Pusher (as simple as possible)

Mihkel Allorg
Jan 6 · 5 min read

This article is a small tutorial on how to start broadcasting Laravel notifications with Pusher and receiving them in React. I had some trouble finding all the necessary documentation when I was starting with broadcasting myself. As I was looking into different tutorials/articles at the same time I had a hard time debugging the problems that occured.


I have a working web application which is built with Laravel and React and now I need my users to receive notifications real-time.

Laravel provides the Pusher channel out of the box and I have decided to go with it. At this point I had never worked with sockets (which is kind of sad as I’ve been a (Laravel) dev for 4 years) and I had no idea how to approach this.

I divided the task into a bunch of smaller tasks:

  • Set up Pusher & broadcasting in Laravel

Set up Pusher broadcasting in Laravel

We can break this task down to smaller too:

  • Install Pusher

First of all we need to add Pusher composer package to the project.

composer require pusher/pusher-php-server "~3.0"

Secondly let’s set up config files and the environment variables.

The config/broadcasting.php file should have pusher option under connections:

'default' => env('BROADCAST_DRIVER', 'null'),'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
],
],
...
],

Now let’s add the necessary environment variables to .env file

BROADCAST_DRIVER=pusher...PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=

We need to uncomment App\Providers\BroadcastServiceProvider.php in config/app.php

'providers' => [        ...    App\Providers\BroadcastServiceProvider.php,    ...
]

Next let’s look into the file we just uncommented. What’s important here is the Broadcast::routes() method, which defines the routes to respond to channel authorization requests. It takes $options as parameter the same way Route::group() takes them so in my case as I am using JWT tokens for authentication, I need the request to pass through my auth:api middleware. Calling the routes method registers broadcasting/auth route to handle the authorization requests.

class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
*
@return void
*/
public function boot()
{
Broadcast::routes(['middleware' => ['auth:api']]);

require base_path('routes/channels.php');
}
}

Simplified version of how broadcasting works is that events are passed through channels and to receive the events we have to subscribe to the channel. That’s something I’m doing in my React application to receive the notifications. You can read more about the channels in the official Laravel documentation. I’m using private channels so that authorization is needed to receive the events. Along with the events we can pass extra data.

We need to define our channel authorization logic. There’s already an example channel defined for us in routes/channels.php file. I only changed the channel name a little.

Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});

Each user has its own channel and in this example only the user itself can subscribe to its channel.

Next we need to set up our User model. Specifically we need to do two things:

  • Use Notifiable trait
use Illuminate\Notifications\Notifiable;/**
* The channels the user receives notification broadcasts on.
*
*
@return string
*/
public function receivesBroadcastNotificationsOn()
{
return 'App.User.' . $this->id;
}

Create Notification class(es)

Let’s say we have a system where users can post articles and comment on said articles. In case someone adds a comment to my article I’d like to receive a notification about it. So our TODO list looks like this:

  • Create the notification class

In the notification class we have to define a few broadcasting specific methods like toBroadcast method where we can decide which data to pass on with the notification. In case you are using Laravel 5.7, we can specify the broadcast event type and it makes binding to event types a little nicer.

<?php

namespace
Guava\Communication\Notifications;

use App\Models\Comment;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\BroadcastMessage;

class NewComment extends Notification implements ShouldQueue
{
use Queueable;
/** @var Comment */
private $comment;

public function __construct(Comment $comment)
{
$this->comment = $comment;
}
public function via($notifiable)
{
return ['database', 'broadcast'];
}
public function toArray($notifiable)
{
return [
'post' => [
'id' => $this->comment->post_id,
],
'author' => [
'id' => $this->comment->user_id,
'first_name' => $this->comment->user->first_name,
'last_name' => $this->comment-user->last_name,
],
'comment' => [
'id' => $this->comment->id,
'body' => $this->comment->body,
'commented_at' => $this->comment->commented_at,
],
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage($this->toArray($notifiable));
}
public function broadcastType()
{
return 'new-comment';
}

}

In my case I also want to use the database notifications and want them to be structurally the same with the BroadcastMessage so that’s why I’m using the toArray method to define the returned data. If you do not want to do this, just remove the database from via and toArray method.

I have created a simplified endpoint for storing the comment and notifying the user.

public function store(Post $post, StoreCommentRequest $request)
{
$comment = $post->addComment($request->all());
$post->author()->notify(NewComment($comment)); return new CommentResource($comment);
}

To debug and test if everything works, setBROADCAST_DRIVER=log in .env file and see if adding a comment successfully adds the info about the broadcasted message to the logs.

Add Pusher to React

npm install --save pusher-js laravel-echo

I’ll present a simplified version on how to initialize the Echo instance and how to subscribe to channels.

import Echo from 'laravel-echo';const options = {
broadcaster: 'pusher',
key: config.pusher.key,
cluster: config.pusher.cluster,
forceTLS: config.pusher.tls,
//authEndpoint is your apiUrl + /broadcasting/auth
authEndpoint: config.pusher.authEndpoint,
// As I'm using JWT tokens, I need to manually set up the headers.
auth: {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
},
};

const echo = new Echo(options);
echo.private(`App.User.${userId}`).notification((data) => {
console.log(data);
});

The notifications are passed through a private channel. Laravel Echo is a lot more convenient option compared to PusherJS package when using Laravel notifications for broadcasting because there’s a helper function notification which makes subscribing to the notification a breeze. Otherwise you’d have to subscribe to an event called Illuminate\Notifications\Events\BroadcastNotificationCreated and go on from there.

I hope this helps you out, I did spend almost the whole day when starting from scratch without much prior knowledge about sockets, broadcasting and Laravel notifications. Most of the info here can be found on the official Laravel documentation about broadcasting notifications and broadcasting overall.

I’m totally open to all kinds of feedback and ways to create cleaner and more efficient code. I’ve only used React a few months so I’ve got a lot to learn.

Mihkel Allorg

Written by

Writing about your work makes you reconsider and analyze it over and over. So I’m trying to write about the most critical parts of mine.