Building Real-Time Applications with Laravel WebSockets: A Comprehensive Guide

Computer, Electronics, Pc, Laptop, Computer Hardware, Hardware, Monitor, Screen, Clapperboard
Headshot image of author

Mar 26th, 2023 By Taylor Perkins Full Stack Developer

Introduction

Real-time applications have become increasingly popular in recent years due to their ability to provide instant updates and notifications to users. With the advent of WebSockets, building real-time applications has become even more accessible and efficient. In this article, we'll explore how to use Laravel WebSockets to build real-time applications, such as chat applications, sports scoreboards, ride-sharing apps, and social media feeds.

What are WebSockets?

WebSockets are a protocol for creating real-time, bi-directional communication channels between clients (e.g., web browsers) and servers. Unlike traditional HTTP requests, which are one-way, WebSockets allow the server to push updates to clients in real-time. This makes them ideal for building applications that require real-time updates, such as chat applications, online games, and stock market tickers.

What is Laravel WebSockets?

Laravel WebSockets is a package that provides a simple and easy-to-use interface for working with WebSockets in Laravel applications. It's built on top of the Ratchet library, which is a PHP library for working with WebSockets.

Before we dive into using Laravel WebSockets, let's make sure we have everything we need to get started.

  • A working knowledge of Laravel
  • A basic understanding of WebSockets
  • A server with PHP installed

Setting up Laravel WebSockets

The first step in building a real-time application with Laravel WebSockets is to install the package. We can do this using Composer, as follows:

1composer require beyondcode/laravel-websockets

Once the package is installed, we need to publish its configuration file by running the following command:

1php artisan vendor:publish --provider="BeyondCode\\LaravelWebSockets\\WebSocketsServiceProvider" --tag="config"

This will publish a new configuration file, config/websockets.php, which we can use to configure Laravel WebSockets.

Configuring Laravel WebSockets

In the websockets.php configuration file, we can configure various aspects of Laravel WebSockets, such as the host and port it should listen on and the SSL certificate it should use for secure connections.

Let's start by configuring the host and port. By default, Laravel WebSockets will listen on port 6001. If we want to change this, we can do so by setting the LARAVEL_WEBSOCKETS_PORT environment variable to the desired port number. For example, to listen on port 8080, we can add the following line to our .env file:

1LARAVEL_WEBSOCKETS_PORT=8080

Laravel Websockets is a drop-in pusher replacement, which means the easiest way to get started is by using it in combination with Pusher. Let's start by installing the pusher server package using composer.

1composer require pusher/pusher-php-server

We will need to ensure Laravel Websockets is using the Pusher broadcaster, since this is the easiest way to get started. To achieve this, we can simply add the following line to our .env file (or update if already present).

1BROADCAST_DRIVER=pusher

Since we are using the pusher broadcaster, we will need to update the pusher configuration. The default behavior is to send the event information to the official Pusher server, which we don't want in this case. Let's add a host and port configuration key to our example configuration at config/broadcasting.php:

1'pusher' => [
2 'driver' => 'pusher',
3 'key' => env('PUSHER_APP_KEY'),
4 'secret' => env('PUSHER_APP_SECRET'),
5 'app_id' => env('PUSHER_APP_ID'),
6 'options' => [
7 'cluster' => env('PUSHER_APP_CLUSTER'),
8 'encrypted' => true,
9 'host' => '127.0.0.1',
10 'port' => 6001,
11 'scheme' => 'http'
12 ],
13],

Note that the SSL certificate is optional and not required for local development. If we want to use SSL for secure connections, we can set the LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT and LARAVEL_WEBSOCKETS_SSL_LOCAL_PK environment variables to the path of our SSL certificate and private key files, respectively.

Starting Laravel WebSockets

Now that we've configured Laravel WebSockets, we can start the server by running the following command:

1php artisan websockets:serve

This will start the server and listen. Once you have the Laravel WebSockets server up and running, you can start broadcasting events to connected clients. To do this, you'll first need to define an event that you want to broadcast. You can do this by creating a new event class in your Laravel application, and defining any data that you want to send along with the event.

Configuring Laravel Echo

To use Laravel WebSockets in your application, you will also need to configure Laravel Echo, a JavaScript library that allows you to subscribe to channels and listen for events broadcast by the server. Laravel Echo is built on top of the WebSocket protocol and provides a simple API for subscribing to channels and binding callbacks to events. In other words, Laravel Echo acts as a bridge between your application and the Laravel WebSockets server, making it easy to consume real-time data from your server in your JavaScript code. First, you'll need to install Laravel Echo and the pusher-js library.

1npm install --save laravel-echo pusher-js

To configure Laravel Echo, you will need to update your bootstrap.js file to include the appropriate settings. Specifically, you will need to set the broadcaster option to 'pusher', since Laravel Echo is designed to work with the Pusher service that Laravel WebSockets uses under the hood. You will also need to provide your Pusher key and cluster, as well as any other options you want to customize.

Pro Tip: In the following example, it is extremely important to note that not all configurations for every environment will match. The important configuration options to note here are wsPort and wsHost. If using a secure connection, Laravel Echo will always default to using wss. It may benefit you to simply change one config option at a time, and see how the request changes on the frontend. Observe the protocal (ws or wss), the host, and port number.
1window.Echo = new Echo({
2 broadcaster: 'pusher',
3 key: process.env.MIX_PUSHER_APP_KEY,
4 cluster: process.env.MIX_PUSHER_APP_CLUSTER,
5 wsHost: 'blogsockettest.test',
6 wsPort: 6001,
7 forceTLS: false,
8 disableStats: true,
9});

Configuring Laravel Websockets For Valet

It is impossible to give a tutorial on every local development setup. That said, I thought it would be beneficial to show my own example of getting Laravel Websockets to play nice with valet. I first created and secured my valet link. My local dev environment is accessible at https://blogsockettest.test. Let's first configure Laravel Websockets to handle SSL connections. The security certificate that valet gives you is self-signed, which means you may run into some issues when browsers or curl attempts to make those https connections. Let's find our way around that. First, in config/websockets.php we want to turn off verify_peer in the ssl array config.

1'ssl' => [
2 'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),
3 'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),
4 'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
5 
6 'verify_peer' => false,
7],

Next, we will need to tell Laravel Websockets to use our local self-signed valet certificate. These typically live inside the /Users/USER_NAME_HERE/.config/valet/Certificates/ directory. Since my valet link is at blogsockettest.test, I just add these two lines to my .env file:

1LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT=/Users/Taylor/.config/valet/Certificates/BlogSocketTest.test.crt
2LARAVEL_WEBSOCKETS_SSL_LOCAL_PK=/Users/taylor/.config/valet/Certificates/BlogSocketTest.test.key

Now we need to tweak the pusher configuration just a bit so that it doesn't complain about the self-signed certificate. We will pass in curl_options to disable verifying peer or host.

1'pusher' => [
2 'driver' => 'pusher',
3 'key' => env('PUSHER_APP_KEY'),
4 'secret' => env('PUSHER_APP_SECRET'),
5 'app_id' => env('PUSHER_APP_ID'),
6 'options' => [
7 'cluster' => env('PUSHER_APP_CLUSTER'),
8 'encrypted' => true,
9 'host' => 'blogsockettest.test',
10 'port' => 6001,
11 'scheme' => 'https',
12 'useTLS' => true,
13 'curl_options' => [
14 CURLOPT_SSL_VERIFYHOST => 0,
15 CURLOPT_SSL_VERIFYPEER => 0,
16 ]
17 ],
18 ],
Pro Tip: The Pusher cluster, app key, secret and id don't actually matter in the case of using Laravel Websockets like this, so long as they are unique amongst multiple projects that use it.

Now for the part that caused me plenty of grief. Let's configure Laravel Echo to listen to our Laravel Websockets server locally and not the Pusher server. For example, in our bootstrap.js file we have the following configuration.

1window.Echo = new Echo({
2 broadcaster: 'pusher',
3 key: process.env.MIX_PUSHER_APP_KEY,
4 cluster: process.env.MIX_PUSHER_APP_CLUSTER,
5 wsHost: 'blogsockettest.test',
6 wsPort: 6001,
7 wssPort: 6001,
8 forceTLS: false,
9 disableStats: true,
10});

The final key tricky part here, depending on your browser, you may notice you are still having issues. A request such as wss://blogsockettest.test:6001/app/ABCDEFG?protocol=7&client=js&version=8.0.1&flash=false is being made, yet it doesn't return a successful response. The problem I ran into here, was that I had accepted the security warning from Firefox about my self-signed certificate, but I had not accepted it at port 6001. Make a GET request with your browser at https://blogsockettest.test:6001 and accept the security warning. Hopefully you just made your first successful connection to Laravel Websockets.

Broadcasting Events on Laravel Websockets

Once you have your event defined, you can use Laravel's built-in broadcasting functionality to broadcast the event to any connected WebSocket clients. To do this, you can use the broadcast function provided by Laravel, along with the name of the event that you want to broadcast. You can also include any data that you want to send along with the event as a second argument to the broadcast function.

For example, let's say you have an event called NewMessage that you want to broadcast to all connected WebSocket clients. You could define the event like this:

1namespace App\Events;
2 
3use Illuminate\Broadcasting\InteractsWithSockets;
4use Illuminate\Broadcasting\Channel;
5use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6 
7class NewMessage implements ShouldBroadcast
8{
9 use InteractsWithSockets;
10 
11 public $message;
12 
13 public function __construct($message)
14 {
15 $this->message = $message;
16 }
17 
18 public function broadcastOn()
19 {
20 return new Channel('public-chat');
21 }
22}

Then, to broadcast the event to all connected WebSocket clients, you could use the following code:

1use App\Events\NewMessage;
2 
3$newMessage = new NewMessage('Hello, world!');
4broadcast($newMessage)->toOthers();

This would broadcast the NewMessage event with the message "Hello, world!" to all other connected clients on the chat channel.

Broadcasting Events on a Private Channel

To broadcast events on a private authenticated channel using Laravel WebSockets, you can make use of the PrivateChannel class provided by the Laravel framework. First, you need to define the private channel in your channels.php file. For example, to create a private channel named chat, with specific rooms, you can add the following code to your channels.php file; Where $user->hasAccessToChatRoom($roomId) is just simply pseudo code returning a boolean whether the user has access or not:

1use Illuminate\Support\Facades\Broadcast;
2 
3Broadcast::channel('chat.{roomId}', function (User $user, $roomId) {
4 return $user->hasAccessToChatRoom($roomId);
5});

Please Note: You must be signed in as a standard Laravel User here in order to authenticate on a private channel. You can of course authenticate through other methods, but that is beyond the scope of this article. That is all it is doing under the hood, and our callback here just determines whether that specific user should be allowed. Returning true here for testing purposes is completely valid.

Next, You will need to define the Event that utilizes the PrivateChannel class. The following is a simple example showing how to define the broadcastOn to a specific private chat room:

1namespace App\Events;
2 
3use Illuminate\Broadcasting\InteractsWithSockets;
4use Illuminate\Broadcasting\PrivateChannel;
5use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6 
7class NewChatMessage implements ShouldBroadcast
8{
9 use InteractsWithSockets;
10 
11 public $message;
12 
13 public $roomId;
14 
15 public function __construct($roomId, $message)
16 {
17 $this->roomId = $roomId;
18 $this->message = $message;
19 }
20 
21 public function broadcastOn()
22 {
23 return new PrivateChannel(sprintf('chat.%1$s', $this->roomId));
24 }
25}

Finally, in your controller or event, you can use the broadcast helper with the toOthers method to broadcast an event to all other connected clients on the private channel:

1use App\Events\NewChatMessage;
2 
3broadcast(new NewChatMessage(1, 'testMessage'))->toOthers();

This will broadcast the NewChatMessage event to all other connected clients on the chat channel, excluding the current user.

Listening to Laravel Websockets using Laravel Echo

Now that we have everything setup, Laravel Echo is configured and Laravel Websockets is receiving events on the server side, its time to listen to those events and do something with them. We are going to show a simple example where our messages being sent over the private and public channels are received and a js alert is shown. In order to listen to a public channel, we can just use the channel method followed by listen. For Example, were we to listen to our NewMessage event, it would look like this in our bootstrap.js:

1window.Echo.channel('public-chat')
2 .listen('NewMessage', (e) => {
3 console.log(e);
4 alert(e?.message)
5 })

Private channels are only slightly different, as they make a POST request to the default broadcasting auth route at /broadcasting/auth first. Instead of using the channel method, we use the private method. In the following example, We are going to assume an event was broadcast on the chat channel, with roomId of 1. Implementations of listening to a private channel like this would likely be a dynamic channel/roomId number.

1window.Echo.private('chat.1')
2 .listen('NewChatMessage', (e) => {
3 console.log(e);
4 alert(e?.message)
5 })

Conclusion

In this article, we explored the basics of Laravel WebSockets and how it can be used to build real-time applications. We discussed how to install and configure Laravel WebSockets and explored some of its key features, such as channels and events. We also demonstrated how to integrate Laravel WebSockets with Laravel Echo, a JavaScript library that simplifies real-time communication with WebSockets.

Overall, Laravel WebSockets is a powerful tool for building real-time applications and can greatly improve the user experience of your web applications. If you're interested in learning more about Laravel WebSockets, we recommend checking out the official Laravel WebSockets documentation or exploring some of the available packages and examples on GitHub. By implementing Laravel WebSockets in your applications, you can provide your users with a seamless and engaging experience that keeps them coming back for more.

If you have any questions or comments, feel free to leave them below or reach out to me directly.

Happy coding!


Please sign in or create an account to join the conversation.