Setting up Websockets with a separate API and client can be pretty tricky. By the end of this article, you'll be able to listen for realtime events fired from your Laravel app, in Nuxt.
If you need to authenticate on private channels, there's a course on Codecourse that'll guide you through that.
Even if you've got a Laravel project ready to go, I'd recommend starting fresh to avoid anything you've done already getting in the way.
Let's install Laravel with the official Laravel Installer.
laravel new api
Switch your database configuration over and migrate.
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=laravelwebsocketsnuxt
DB_USERNAME=alexgarrettsmith
DB_PASSWORD=
php artisan migrate
Now serve your Laravel app. To keep things simple, we'll be using PHP's built-in webserver.
php artisan serve
Open this in your browser with the localhost
domain. When we set our Nuxt project up, we'll use the same domain (this is important).
Next, we'll install the Laravel Websockets package and verify that we're broadcasting events properly.
First, install with Composer.
composer require beyondcode/laravel-websockets
Publish the migration for websocket statistics, and migrate.
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
php artisan migrate
We're not going to be touching the configuration file for this package, but let's publish it anyway.
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
Because we're using Laravel Websockets as a Pusher replacement, we'll also need to pull in the Pusher PHP SDK.
composer require pusher/pusher-php-server "~3.0"
Head over to .env
and switch the BROADCAST_DRIVER
to pusher
.
BROADCAST_DRIVER=pusher
While we're in .env
, we'll set an app ID, key and secret for Pusher. These are what we'd normally use to connect to Pusher, but since we're replacing Pusher with the Laravel Websockets package, any values will work here for local development.
PUSHER_APP_ID=local
PUSHER_APP_KEY=local
PUSHER_APP_SECRET=local
PUSHER_APP_CLUSTER=mt1
Now head over to config/broadcasting.php
and update the pusher configuration to hit our local websocket server. Under connections
, the pusher
configuration should look like this.
'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'),
'encrypted' => true,
'host' => '127.0.0.1',
'port' => 6001,
'scheme' => 'http'
],
],
For basic usage, that's pretty much it! Let's run the websocket server and make sure we can access the statistics dashboard.
php artisan websockets:serve
Making sure you still have php artisan serve
running from earlier, open the websocket dashboard in your browser. This is found at http://localhost:8000/laravel-websockets.
Hit the Connect button, and you should see something like this.
If that's working, your websocket server is configured properly, and we can test dispatching an event.
To test we're seeing events roll into the websocket dashboard (and eventually in our Nuxt app), let's create a really simple event.
php artisan make:event PostCreated
Open this up, and implement the ShouldBroadcast
interface.
class PostCreated implements ShouldBroadcast
{
// ...
}
Because we're only covering public channels here, change the broadcastOn
method to look like this.
public function broadcastOn()
{
return new Channel('posts');
}
Now head over to your routes/web.php
file and add the following route, to broadcast the new event we've created.
Route::get('/broadcast', function () {
broadcast(new PostCreated());
});
Tip: Instead of registering a route, you can also just run the broadcast code in php artisan tinker
, or Tinkerwell if you have it.
Ok, let's test that our event is being broadcast!
Make sure you have the websocket dashboard open from earlier, and hit http://localhost:8000/broadcast in another tab. You should see an api-message
roll in on the websocket dashboard.
If that's working, you're ready to get a fresh Nuxt project set up to listen for events.
Again, if you already have a Nuxt app ready to go, I'd recommend creating a fresh one just to test this stuff out.
npm init nuxt-app client
Run through the installation steps as you need. There's nothing specific you'll need to choose here for the purpose of getting websockets connected.
Once Nuxt has been installed, serve it.
npm run dev
Open it up in your browser, making sure you use the localhost
domain so it matches the API.
If you've worked with broadcasting in a Laravel app before, you've likely used Laravel Echo.
If you haven't, this package handles the connection to a websocket server, with the ability to listen on a channel, for events. In our case, we're going to listen to our API's websocket server on the posts
channel, for the PostCreated
event.
Normally we'd attach this to the browser window
object, but we'll create a Nuxt plugin so we can use this easily in our Nuxt pages/components/store.
First up, we'll need to install Laravel Echo and the Pusher SDK for Javascript.
npm install laravel-echo pusher-js
We're pulling in the Pusher Javascript SDK because we're using Laravel Websockets as a Pusher replacement, not actually hitting Pusher's servers.
Now create an echo.js
file under the plugins
directory in Nuxt and add the following.
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js')
export default (_, inject) => {
const echo = new Echo({
broadcaster: 'pusher',
key: 'local', // .env
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
disableStats: true
})
inject('echo', echo)
}
We're doing a few things here, so let's break it down.
wsHost
will resolve to localhost
. disableStats
is used so we don't send statistic information to Pusher's servers. The key
is set as local
, which you'll have added earlier to your .env
file in Laravel. I'd recommend you set this inside environment variables in Nuxt at some stage.inject
the Echo instance. This makes it available to all pages, components, store actions and other plugins in Nuxt.Now we've created the plugin, we'll register it in nuxt.config.js
plugins: [
{ src: './plugins/echo', mode: 'client' }
],
Notice we're setting the mode
to client
. This ensures it's not used on the server-side of Nuxt (It just wouldn't work, because we're accessing window
in our plugin).
You've now created the plugin to listen to events from the websocket server. Let's actually use it!
Let's use the default index.vue
page in Nuxt to start listening. First up, clear it out so it looks like this.
<template>
<div></div>
</template>
<script>
export default {
}
</script>
Once the page component has mounted, we can now use our injected Echo instance to listen on a specific channel, to a specific event.
export default {
mounted () {
this.$echo.channel('posts')
.listen('PostCreated', (e) => {
console.log(e)
})
}
}
And, that's it! Open up your Nuxt app homepage in the browser and separately visit http://localhost:8000/broadcast to broadcast the PostCreated
event.
You should see the details for the event logged out to the console.
At the moment we don't have any data attached to this event, so it's just an empty array. Once you start adding public properties to the PostCreated
event, they'll appear in this payload.
You're now successfully listening to broadcasted events. There's a slight issue we'll need to resolve, though.
It's likely you'll allow your users to navigate to different pages in your Nuxt app. As users navigate back and forth between pages that are listening on a websocket channel, we'll actually connect more than once.
If you'd like to test this, create another page in your Nuxt app with some simple navigation at the top of the page and navigate back to the index.vue
page a few times. Fire the PostCreated
event from the API again and you'll see the empty array from the PostCreated
event logged out more than once.
Not good.
To solve this, we'll *leave *the posts
channel whenever we navigate away from the index.vue
page. If you're listening to events from components in your Nuxt app, the same solution applies.
Update the index.vue
page with the following.
export default {
// ...
beforeDestroy () {
this.$echo.leave('posts')
}
}
This uses Echo to leave the posts
channel when the component (in this case, a page) is destroyed, so we're now disconnecting and reconnecting whenever we navigate away from and back to a page.
If this feels weird or wasteful to disconnect and reconnect every time, consider how this would work in a non-SPA (single page application) site. It would do the same!
From start to finish, this guide got you set up with broadcasting and listening to events on public channels with Laravel Websockets and Nuxt.
I've covered authenticating on private channels in the Laravel Websockets with Nuxt course over on Codecourse.
Happy broadcasting!