Broadcasting
Introduction
In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. WebSockets provide a more efficient alternative to continually polling your application's server for data changes that should be reflected in your UI.
For example, imagine your application is able to export a user's data to a CSV file and email it to them. However, creating this CSV file takes several minutes so you choose to create and mail the CSV within a queued job. When the CSV has been created and mailed to the user, we can use event broadcasting to dispatch an App\Events\UserDataExported event that is received by our application's JavaScript. Once the event is received, we can display a message to the user that their CSV has been emailed to them without them ever needing to refresh the page.
To assist you in building these types of features, Laravel makes it easy to "broadcast" your server-side Laravel events over a WebSocket connection. Broadcasting your Laravel events allows you to share the same event names and data between your server-side Laravel application and your client-side JavaScript application.
The core concepts behind broadcasting are simple: clients connect to named channels on the frontend, while your Laravel application broadcasts events to these channels on the backend. These events can contain any additional data you wish to make available to the frontend.
Supported Drivers
By default, Laravel includes three server-side broadcasting drivers for you to choose from: Laravel Reverb, Pusher Channels, and Ably.
Before diving into event broadcasting, make sure you have read Laravel's documentation on events and listeners.
Server Side Installation
To get started using Laravel's event broadcasting, we need to do some configuration within the Laravel application as well as install a few packages.
Event broadcasting is accomplished by a server-side broadcasting driver that broadcasts your Laravel events so that Laravel Echo (a JavaScript library) can receive them within the browser client. Don't worry - we'll walk through each part of the installation process step-by-step.
Configuration
All of your application's event broadcasting configuration is stored in the config/broadcasting.php configuration file. Don't worry if this directory does not exist in your application; it will be created when you run the install:broadcasting Artisan command.
Laravel supports several broadcast drivers out of the box: Laravel Reverb, Pusher Channels, Ably, and a log driver for local development and debugging. Additionally, a null driver is included which allows you to disable broadcasting during testing. A configuration example is included for each of these drivers in the config/broadcasting.php configuration file.
Installation
By default, broadcasting is not enabled in new Laravel applications. You may enable broadcasting using the install:broadcasting Artisan command:
php artisan install:broadcasting
The install:broadcasting command will create the config/broadcasting.php configuration file. In addition, the command will create the routes/channels.php file where you may register your application's broadcast authorization routes and callbacks.
Queue Configuration
Before broadcasting any events, you should first configure and run a queue worker. All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected by events being broadcast.
Reverb
When running the install:broadcasting command, you will be prompted to install Laravel Reverb. Of course, you may also install Reverb manually using the Composer package manager.
composer require laravel/reverb
Once the package is installed, you may run Reverb's installation command to publish the configuration, add Reverb's required environment variables, and enable event broadcasting in your application:
php artisan reverb:install
You can find detailed Reverb installation and usage instructions in the Reverb documentation.
Pusher Channels
If you plan to broadcast your events using Pusher Channels, you should install the Pusher Channels PHP SDK using the Composer package manager:
composer require pusher/pusher-php-server
Next, you should configure your Pusher Channels credentials in the config/broadcasting.php configuration file. An example Pusher Channels configuration is already included in this file, allowing you to quickly specify your key, secret, and application ID. Typically, you should configure your Pusher Channels credentials in your application's .env file:
PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"
The config/broadcasting.php file's pusher configuration also allows you to specify additional options that are supported by Channels, such as the cluster.
Then, set the BROADCAST_CONNECTION environment variable to pusher in your application's .env file:
BROADCAST_CONNECTION=pusher
Finally, you are ready to install and configure Laravel Echo, which will receive the broadcast events on the client-side.
Ably
The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please consult Ably's Laravel broadcaster documentation.
If you plan to broadcast your events using Ably, you should install the Ably PHP SDK using the Composer package manager:
composer require ably/ably-php
Next, you should configure your Ably credentials in the config/broadcasting.php configuration file. An example Ably configuration is already included in this file, allowing you to quickly specify your key. Typically, this value should be set via the ABLY_KEY environment variable:
ABLY_KEY=your-ably-key
Then, set the BROADCAST_CONNECTION environment variable to ably in your application's .env file:
BROADCAST_CONNECTION=ably
Finally, you are ready to install and configure Laravel Echo, which will receive the broadcast events on the client-side.
Client Side Installation
Reverb
Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the pusher-js package since Reverb utilizes the Pusher protocol for WebSocket subscriptions, channels, and messages:
npm install --save-dev laravel-echo pusher-js
Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the resources/js/bootstrap.js file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file - you simply need to uncomment it and update the broadcaster configuration option to reverb:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT,
wssPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
Next, you should compile your application's assets:
npm run build
The Laravel Echo reverb broadcaster requires laravel-echo v1.16.0+.
Pusher Channels
Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. Echo also leverages the pusher-js NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages.
The install:broadcasting Artisan command automatically installs the laravel-echo and pusher-js packages for you; however, you may also install these packages manually via NPM:
npm install --save-dev laravel-echo pusher-js
Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The install:broadcasting command creates an Echo configuration file at resources/js/echo.js; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Pusher:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
forceTLS: true
});
Next, you should define the appropriate values for the Pusher environment variables in your application's .env file. If these variables do not already exist in your .env file, you should add them:
PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"
VITE_APP_NAME="${APP_NAME}"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
Once you have adjusted the Echo configuration according to your application's needs, you may compile your application's assets:
npm run build
To learn more about compiling your application's JavaScript assets, please consult the documentation on Vite.
Using an Existing Client Instance
If you already have a pre-configured Pusher Channels client instance that you would like Echo to utilize, you may pass it to Echo via the client configuration option:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
const options = {
broadcaster: 'pusher',
key: 'your-pusher-channels-key'
}
window.Echo = new Echo({
...options,
client: new Pusher(options.key, options)
});
Ably
The documentation below discusses how to use Ably in "Pusher compatibility" mode. However, the Ably team recommends and maintains a broadcaster and Echo client that is able to take advantage of the unique capabilities offered by Ably. For more information on using the Ably maintained drivers, please consult Ably's Laravel broadcaster documentation.
Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. Echo also leverages the pusher-js NPM package to implement the Pusher protocol for WebSocket subscriptions, channels, and messages.
The install:broadcasting Artisan command automatically installs the laravel-echo and pusher-js packages for you; however, you may also install these packages manually via NPM:
npm install --save-dev laravel-echo pusher-js
Before continuing, you should enable Pusher protocol support in your Ably application settings. You may enable this feature within the "Protocol Adapter Settings" portion of your Ably application's settings dashboard.
Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. The install:broadcasting command creates an Echo configuration file at resources/js/echo.js; however, the default configuration in this file is intended for Laravel Reverb. You may copy the configuration below to transition your configuration to Ably:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443,
disableStats: true,
encrypted: true,
});
You may have noticed our Ably Echo configuration references a VITE_ABLY_PUBLIC_KEY environment variable. This variable's value should be your Ably public key. Your public key is the portion of your Ably key that occurs before the : character.
Once you have adjusted the Echo configuration according to your needs, you may compile your application's assets:
npm run dev
To learn more about compiling your application's JavaScript assets, please consult the documentation on Vite.
Concept Overview
Laravel's event broadcasting allows you to broadcast your server-side Laravel events to your client-side JavaScript application using a driver-based approach to WebSockets. Currently, Laravel ships with Pusher Channels and Ably drivers. The events may be easily consumed on the client-side using the Laravel Echo JavaScript package.
Events are broadcast over "channels", which may be specified as public or private. Any visitor to your application may subscribe to a public channel without any authentication or authorization; however, in order to subscribe to a private channel, a user must be authenticated and authorized to listen on that channel.
Using an Example Application
Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example.
In our application, let's assume we have a page that allows users to view the shipping status for their orders. Let's also assume that an OrderShipmentStatusUpdated event is fired when a shipping status update is processed by the application:
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
The ShouldBroadcast Interface
When a user is viewing one of their orders, we don't want them to have to refresh the page to view status updates. Instead, we want to broadcast the updates to the application as they are created. So, we need to mark the OrderShipmentStatusUpdated event with the ShouldBroadcast interface. This will instruct Laravel to broadcast the event when it is fired:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class OrderShipmentStatusUpdated implements ShouldBroadcast
{
/**
* The order instance.
*
* @var \App\Models\Order
*/
public $order;
}
The ShouldBroadcast interface requires our event to define a broadcastOn method. This method is responsible for returning the channels that the event should broadcast on. An empty stub of this method is already defined on generated event classes, so we only need to fill in its details. We only want the creator of the order to be able to view status updates, so we will broadcast the event on a private channel that is tied to the order:
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
/**
* Get the channel the event should broadcast on.
*/
public function broadcastOn(): Channel
{
return new PrivateChannel('orders.'.$this->order->id);
}
If you wish the event to broadcast on multiple channels, you may return an array instead:
use Illuminate\Broadcasting\PrivateChannel;
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.'.$this->order->id),
// ...
];
}
Authorizing Channels
Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in our application's routes/channels.php file. In this example, we need to verify that any user attempting to listen on the private orders.1 channel is actually the creator of the order:
use App\Models\Order;
use App\Models\User;
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
The channel method accepts two arguments: the name of the channel and a callback which returns true or false indicating whether the user is authorized to listen on the channel.
All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the {orderId} placeholder to indicate that the "ID" portion of the channel name is a wildcard.
Listening for Event Broadcasts
Next, all that remains is to listen for the event in our JavaScript application. We can do this using Laravel Echo. First, we'll use the private method to subscribe to the private channel. Then, we may use the listen method to listen for the OrderShipmentStatusUpdated event. By default, all of the event's public properties will be included on the broadcast event:
Echo.private(`orders.${orderId}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order);
});
Defining Broadcast Events
To inform Laravel that a given event should be broadcast, you must implement the Illuminate\Contracts\Broadcasting\ShouldBroadcast interface on the event class. This interface is already imported into all event classes generated by the framework so you may easily add it to any of your events.
The ShouldBroadcast interface requires you to implement a single method: broadcastOn. The broadcastOn method should return a channel or array of channels that the event should broadcast on. The channels should be instances of Channel, PrivateChannel, or PresenceChannel. Instances of Channel represent public channels that any user may subscribe to, while PrivateChannels and PresenceChannels represent private channels that require channel authorization:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
class ServerCreated implements ShouldBroadcast
{
use SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public User $user,
) {}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('user.'.$this->user->id),
];
}
}
After implementing the ShouldBroadcast interface, you only need to fire the event as you normally would. Once the event has been fired, a queued job will automatically broadcast the event using your specified broadcast driver.
Broadcast Name
By default, Laravel will broadcast the event using the event's class name. However, you may customize the broadcast name by defining a broadcastAs method on the event:
/**
* The event's broadcast name.
*/
public function broadcastAs(): string
{
return 'server.created';
}
If you customize the broadcast name using the broadcastAs method, you should make sure to register your listener with a leading . character. This will instruct Echo to not prepend the application's namespace to the event:
.listen('.server.created', function (e) {
....
});
Broadcast Data
When an event is broadcast, all of its public properties are automatically serialized and broadcast as the event's payload, allowing you to access any of its public data from your JavaScript application. So, for example, if your event has a single public $user property that contains an Eloquent model, the event's broadcast payload would be:
{
"user": {
"id": 1,
"name": "Patrick Stewart"
...
}
}
However, if you wish to have more fine-grained control over your broadcast payload, you may add a broadcastWith method to your event. This method should return the array of data that you wish to broadcast as the event payload:
/**
* Get the data to broadcast.
*
* @return array<string, mixed>
*/
public function broadcastWith(): array
{
return ['id' => $this->user->id];
}
Broadcast Queue
By default, each broadcast event is placed on the default queue for the default queue connection specified in your queue.php configuration file. You may customize the queue connection and name used by the broadcaster by defining connection and queue properties on your event class:
/**
* The name of the queue connection to use when broadcasting the event.
*
* @var string
*/
public $connection = 'redis';
/**
* The name of the queue on which to place the broadcasting job.
*
* @var string
*/
public $queue = 'default';
Alternatively, you may customize the queue name by defining a broadcastQueue method on your event:
/**
* The name of the queue on which to place the broadcasting job.
*/
public function broadcastQueue(): string
{
return 'default';
}
If you would like to broadcast your event using the sync queue instead of the default queue driver, you can implement the ShouldBroadcastNow interface instead of ShouldBroadcast:
<?php
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
// ...
}
Broadcast Conditions
Sometimes you want to broadcast your event only if a given condition is true. You may define these conditions by adding a broadcastWhen method to your event class:
/**
* Determine if this event should broadcast.
*/
public function broadcastWhen(): bool
{
return $this->order->value > 100;
}
Broadcasting and Database Transactions
When broadcast events are dispatched within database transactions, they may be processed by the queue before the database transaction has committed. When this happens, any updates you have made to models or database records during the database transaction may not yet be reflected in the database. In addition, any models or database records created within the transaction may not exist in the database. If your event depends on these models, unexpected errors can occur when the job that broadcasts the event is processed.
If your queue connection's after_commit configuration option is set to false, you may still indicate that a particular broadcast event should be dispatched after all open database transactions have been committed by implementing the ShouldDispatchAfterCommit interface on the event class:
<?php
namespace App\Events;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Queue\SerializesModels;
class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
{
use SerializesModels;
}
To learn more about working around these issues, please review the documentation regarding queued jobs and database transactions.
Authorizing Channels
Private channels require you to authorize that the currently authenticated user can actually listen on the channel. This is accomplished by making an HTTP request to your Laravel application with the channel name and allowing your application to determine if the user can listen on that channel. When using Laravel Echo, the HTTP request to authorize subscriptions to private channels will be made automatically.
When broadcasting is enabled, Laravel automatically registers the /broadcasting/auth route to handle authorization requests. The /broadcasting/auth route is automatically placed within the web middleware group.
Defining Authorization Callbacks
Next, we need to define the logic that will actually determine if the currently authenticated user can listen to a given channel. This is done in the routes/channels.php file that was created by the install:broadcasting Artisan command. In this file, you may use the Broadcast::channel method to register channel authorization callbacks:
use App\Models\User;
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
The channel method accepts two arguments: the name of the channel and a callback which returns true or false indicating whether the user is authorized to listen on the channel.
All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the {orderId} placeholder to indicate that the "ID" portion of the channel name is a wildcard.
You may view a list of your application's broadcast authorization callbacks using the channel:list Artisan command:
php artisan channel:list
Authorization Callback Model Binding
Just like HTTP routes, channel routes may also take advantage of implicit and explicit route model binding. For example, instead of receiving a string or numeric order ID, you may request an actual Order model instance:
use App\Models\Order;
use App\Models\User;
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
return $user->id === $order->user_id;
});
Unlike HTTP route model binding, channel model binding does not support automatic implicit model binding scoping. However, this is rarely a problem because most channels can be scoped based on a single model's unique, primary key.
Authorization Callback Authentication
Private and presence broadcast channels authenticate the current user via your application's default authentication guard. If the user is not authenticated, channel authorization is automatically denied and the authorization callback is never executed. However, you may assign multiple, custom guards that should authenticate the incoming request if necessary:
Broadcast::channel('channel', function () {
// ...
}, ['guards' => ['web', 'admin']]);