Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sdwru/2fc0cd414b12cabfb3b84d05bb693375 to your computer and use it in GitHub Desktop.
Save sdwru/2fc0cd414b12cabfb3b84d05bb693375 to your computer and use it in GitHub Desktop.
Laravel-websockets on Laravel API backend + Laravel Echo on Vue.js front end on different server/domain using private channel.md

These are my notes on how I got all the basics working end to end.

Laravel API backend and Vue.js frontend are 2 different servers on different public IP addresses and different domains. I am using pure Vue.js on the front end. Most instructions assume Laravel Vue.js is being used which is structured slightly different with different file names and directories.

  • Laravel API backend domain name (api.somedomain.com)
  • Vue.js frontend domain name (client.somedomain.com)

No database is needed for any of this to work using sync queue. That is only needed in production if/when you use a database queue.


Laravel API backend server (api.somedomain.com)

composer require beyondcode/laravel-websockets
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

Make sure App\Providers\BroadcastServiceProvider::class is uncommented

config/app.php

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

.env

BROADCAST_DRIVER=pusher
QUEUE_CONNECTION=sync
PUSHER_APP_ID=testapp
PUSHER_APP_KEY=websocketkey
PUSHER_APP_SECRET=somethingsecret
PUSHER_APP_CLUSTER=mt1

config/broadcasting.php

'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'),
            //'useTLS' => false,
            'encrypted' => true,
            'host' => '127.0.0.1',
            'port' => 6001,
            'scheme' => 'http',
        ],
    ],
    ...

config/websockets.php

'apps' => [
      [
          'id' => env('PUSHER_APP_ID'),
          'name' => env('APP_NAME'),
          'key' => env('PUSHER_APP_KEY'),
          'secret' => env('PUSHER_APP_SECRET'),
          'path' => env('PUSHER_APP_PATH'),
          'capacity' => null,
          'enable_client_messages' => true,
          'enable_statistics' => true,
      ],
  ],

routes/api.php

Route::get('test', function () {
    event(new App\Events\Test());
    return "Event has been sent!";
});

routes/channels.php

// This is only for testing purposes
Broadcast::channel('testchannel', function ($user) {
    return true;
}); 

// This is probably closer to what most would use in production
Broadcast::channel('user.{id}', function ($user, $id) {
    //return true if api user is authenticated
    return (int) $user->id === (int) $id;
});

app/Http/Middelware/VerifyCsrfToken.php

protected $except = [
    'broadcasting/auth',
];

app/Events/Test.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class Test implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct()
    {
      //
    }

    public function broadcastOn()
    {
        return new PrivateChannel('testchannel');
    }
}

Run websockets server on Laravel api backend

php artisan websockets:serve


Vue.js frontend (client.somedomain.com)

npm install --save laravel-echo pusher-js

.env Note: The variable MUST be prefixed with VUE_APP_ https://cli.vuejs.org/guide/mode-and-env.html#modes

VUE_APP_PUSHER_APP_KEY='websocketkey'

Add Echo globally. In laravel vue that file is usually called bootstrap.js. In pure Vue.js it is called main.js

main.js

import Echo from 'laravel-echo'
window.Pusher = require('pusher-js')

// This assumes you have already saved your bearer token in your browsers local storage
const token = localStorage.getItem('user-token')

window.Echo = new Echo({
  broadcaster: 'pusher',
  key: process.env.VUE_APP_MIX_PUSHER_APP_KEY,
  wsHost: 'api.somedomain.com',
  authEndpoint: 'http://api.somedomain.com/broadcasting/auth',
  encrypted: true,
  forceTLS: false,
  wsPort: 6001,
  wssPort: 6001,
  disableStats: true,
  enabledTransports: ['ws', 'wss'],
  auth: {
    headers: {
      authorization: 'Bearer ' + token,
    }
  }
})

Add the Echo channel to whichever *.vue file we want to test this on

src/App.vue

...
created() {
  window.Echo.private('testchannel')
      .listen('Test', (e) => {
          console.log('test successful ' + e)
  })

All done. Now open your src/App.vue Single Page App in a chrome/firefox browser with the developer/inspect console window opened.

In a separate browser tab/window browse to http://api.somedomain.com/test to fire the test event. You should see "test successful" showing up in your SPA page developer console.

The end.

@cAstraea
Copy link

cAstraea commented Jul 2, 2024

How to keep the connection alive or deal with inactive window ? I'm encountering some strange behaviour that I can't explain

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment