Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

psrz's avatar
Level 10

Laravel Reverb and Nuxt 3

I'm trying to get this rolling and it's not working.

To my surprise, apparently, the issue is with the Laravel application (a 10 upgraded to 11).

I watched https://laracasts.com/series/lukes-larabits/episodes/17 and it looks pretty straightforward.

This is the echo client setup as a nuxt plugin

import axios from 'axios';
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';


export default defineNuxtPlugin(() => {

  console.log('loading echo.client.js');
  window.axios = axios;
  window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';


  window.Pusher = Pusher;
  
  const ws =new Echo({
    broadcaster: 'reverb',
    key: 'uqoroomcqjq8siyejiye',
    wsHost: 'localhost',
    wsPort: 8080,
    wssPort: 443,
    forceTLS: false,
    enabledTransports: ['ws', 'wss'],
  });

  window.Echo = ws;

  return {
    provide: {
      ws
    }
  }
});

This is how I'm susbscribing to a Echo channel

const channel = `App.Models.User.${auth.user.id}`;
const ws = useNuxtApp().$ws;

onMounted(async ()  => {

  await ws.private(channel)
    .listen('tea.updated', (e) => {
      console.log('tea.updated', e);
    });

  console.log('WS::channel() : ',channel);
  console.log('WS::socketId() : ',ws.socketId());
});

onUnmounted(() => {
  ws.leave(channel);
  console.log('WS::leave() : ',channel);
});

This is what I get on the browser console Imgur Imgur

So far so good. I even see the ping from the client once I start the reverb server

 Message Received ......................................................... 431194327.620463464

   1▕ {
   2▕     "event": "pusher:ping",
   3▕     "data": []
   4▕ }

  Message Handled ......................................................... 431194327.620463464

My custom event:

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

    public function __construct(public Tea $tea, protected User $user)
    {
    }

    public function broadcastAs(): string
    {
        ray(__METHOD__ . ':' . __LINE__ . ': TeaEvent::broadcastAs')->green();
        return 'tea.updated';
    }

    public function broadcastOn(): array
    {
        $channel = "App.Models.User.{$this->user->id}";
        ray(__METHOD__ . ':' . __LINE__ . ': TeaEvent::broadcastOn : ' . $channel)->green();

        return [
            new PrivateChannel($channel),
        ];
    }
}

routes/channels.php

// ray(__FILE__.':'.__LINE__.': channels.php')->green();

Broadcast::channel('App.Models.User.{id}', function ($user, $id) {

    ray(__METHOD__.':'.__LINE__.': $user->id = '.$user->id)->green();

    return $user->id === $id;
});

I commented the first ray() call because the channels.php seems to run every 3 or 4 seconds and my Ray client was being spammed....

Finally, a small artisan command to test this out:

Artisan::command('broadcast-test', function () {

    $tea = \App\Models\Tea::inRandomOrder()->first();
    $user = \App\Models\User::find('01hwz799tf6pb866efgs76v2fm');

    \App\Events\TeaEvent::dispatch($tea, $user);
});

I have BROADCAST_CONNECTION=reverb in the .env file.

Both config/broadcasting.php and config/reverb.php are the same as the ones in the larabit's repo from Luke

I start up the queue, I run that command and nothing happens on the nuxt client. Not an error, warning or anything

I see the tick on the queue console, on the Ray app, I see both ray() calls to broadcastAs() and broadcastOn() methods, so that's working.

I don't know what is going on.

The one ray() call that does not show up is the one inside the callback of the route/channels.php. That callback is never executed. I don't know when it should be called but I think at least once it should be.

I'm tailing laravel.log and also nothing there.

In any case, I'm out of ideas.

0 likes
5 replies
LaryAI's avatar
Level 58

It seems like the issue you're encountering might be related to the authentication of private channels in Laravel with your Nuxt client. Since the callback in routes/channels.php is not being executed, it suggests that the authentication for the private channel is failing or not being triggered at all.

Here are a few steps and checks to ensure everything is set up correctly:

  1. Ensure Client Sending Authorization Headers: Make sure that your Nuxt client is correctly sending the necessary authorization headers when trying to subscribe to the private channel. This typically includes the Authorization header with a Bearer token.

  2. Check Laravel Echo Server Configuration: Verify that your Laravel Echo Server (or Reverb in your case) is properly configured to handle private channels. This includes checking the authEndpoint and ensuring it points to the correct URL in your Laravel application that handles authentication.

  3. CORS Issues: Sometimes, Cross-Origin Resource Sharing (CORS) issues can prevent the authentication requests from being properly handled. Ensure that your Laravel application headers allow requests from your Nuxt client's domain.

  4. Logging and Debugging: Add logging in your Laravel application, especially around the authentication parts (e.g., in the BroadcastServiceProvider or directly in the routes/channels.php). This can help determine if the requests are reaching the server and what responses are being generated.

  5. Environment Configuration: Double-check your .env settings related to broadcasting and ensure they are correctly set up as per your application's requirements.

  6. Broadcasting Configuration: Review the config/broadcasting.php to ensure that the connection configurations, especially for reverb, are correctly set up.

Here's a basic checklist and a code snippet to help you debug the issue:

Debugging Steps

  • Add logs in routes/channels.php:

    Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
        \Log::info("Attempting to authenticate user for channel access: User ID {$user->id}, Channel ID: {$id}");
        return (int) $user->id === (int) $id;
    });
    
  • Ensure CORS settings are correct in cors.php or wherever you handle CORS:

    'allowed_origins' => ['http://your-nuxt-client-url.com'],
    
  • Check the network tab in your browser's developer tools to see if the authentication request is being made and what the response is.

Example Echo Configuration in Nuxt Client

Ensure that the Echo configuration in your Nuxt plugin includes the correct headers:

const ws = new Echo({
  broadcaster: 'reverb',
  key: 'your-key',
  wsHost: 'localhost',
  wsPort: 8080,
  wssPort: 443,
  forceTLS: false,
  enabledTransports: ['ws', 'wss'],
  auth: {
    headers: {
      Authorization: `Bearer ${yourTokenHere}`,
      Accept: 'application/json',
    },
  },
});

By following these steps and adding appropriate logging, you should be able to pinpoint whether the issue lies in the authentication process, network configuration, or the client setup.

elbsurfer's avatar

@psrz Did you find the reason on your side?

Have you tried it with shouldBroadcastNow to prevent that the queue is the issue? Or have you tried to just return true in the channels configuration to prevent that the issue is there? I would also try to use a public channel first.

I use Nuxt with a core package and three layers/apps. In my case I added a nuxt plugin to the core package, and added the .env variables to my three apps. Now, I can connect to the websocket server from one of these apps, from the other one it's not working. I get always the error "connection to websocket failed". Have you experienced it with multiple layers in Nuxt as well?

psrz's avatar
Level 10

@elbsurfer

We solved this.

What was failing was the call to the broadcasting/auth from the Echo client. Its default is broadcasting/auth but we are using nuxt proxy to route all calls to the laravel server so we had to adjust that when instantiating the client

  const ws = new Echo({
    broadcaster: 'reverb',
    authEndpoint: '/backend/broadcasting/auth',
    key: '*****************',
    wsHost: config.public.wsHost,
    wsPort: config.public.wsPort,
    wssPort: 443,
    forceTLS: false,
    enabledTransports: ['ws', 'wss'],
  });
psrz's avatar
Level 10

@najeeb-anwari

Yes, it's working with https, but we are not configuring that in the web server.

We have some HAProxy magic running, which I know nothing about, and that's where the https layer is being set. On the Laravel side all we need to do is to properly set the TrustProxies middleware and that's pretty much it.

Please or to participate in this conversation.