Back to articles

Laravel Websockets with Nuxt

February 22nd, 2021

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.

Fresh Laravel Project

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).

Install Laravel Websockets

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.

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.

Fresh Nuxt app

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.

Creating a Nuxt plugin for Laravel Echo

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.

  1. Importing Laravel Echo and the Pusher JavaScript SDK.
  2. Creating a new Laravel Echo instance with configuration for our API. 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.
  3. We 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!

Listening for events

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.

Preventing duplicate channel connections

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!

We're done

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!

Author
Alex Garrett-Smith

Comments

No coments, yet. Be the first to leave a comment.