0

My Laravel 11 application is not broadcasting anything using Laravel Reverb. I am using a custom Docker configuration to spin up my enviornment and Laravel acts as a REST api interacting with a React stand-alone front-end.

My reverb.php file in my config/ directory is untouched as I just updated the .env.

BROADCAST_CONNECTION=reverb
    
REVERB_APP_ID=12345
REVERB_APP_KEY=MYVERYSECRETKEY
REVERB_APP_SECRET=SUPERSECRET
REVERB_HOST="localhost"
REVERB_PORT=9090
REVERB_SCHEME=http

The broadcasting.php file has this set as the driver as from what I gather Laravel Reverb runs it's own config reverb.php.

'default' => env('BROADCAST_DRIVER', 'null'),

When running php artisan queue:listen within my Docker container I can see the Events firing and everything running as it should...

When I run php artisan channel:list I see channel_for_everyone

When running within my Docker container php artisan reverb:start --debug I can see some ping logs.

Connection Established ............ 310096377.635725104  
Message Handled ................... 475763427.215883647  
Message Received .................. 726544741.227378338  
      
{ 
 "event": "pusher:ping", 
 "data": [] 
}

With the front-end in the Network tab again everything looks ok, it pings the /broadcasting/auth and my setup ws://localhost:9090/app/

Request URL: http://localhost:8000/broadcasting/auth
Request Method: POST
Status Code: 200 OK
Remote Address: [::1]:8000
Referrer Policy: strict-origin-when-cross-origin

Request URL: ws://localhost:9090/app/MYVERYSECRETKEY?protocol=7&client=js&version=8.4.0-rc2&flash=false
Request Method: GET OK
Status Code: 101 Switching Protocols

The connection itself seems ok? One thing to note is if I hit the endpoint to fire the event, I see no logs for the broacast php artisan reverb:start --debug

My Event, which is really basic for now and gets caught and logs when runnig queue:listen but never broadcasts, although the the broadcastOn() method gets hit and I can catch with a dd()

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

    public function __construct(
    ) {
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        //dd('inside broadcaseOn() in Event'); <- Gets here
        return [
            new PrivateChannel('channel_for_everyone'), // can see value of channel
        ];
    }
}

Even if I make a channel not private I still get nothing!

public function broadcastOn()
{
    return [
        new Channel('channel_for_everyone'),
    ];
}

in my channels.php the dd() inside the channel_for_everyone never hits, but the file is 100% getting loaded as when I dd() above it will dd('it will hit here!);

dd('it will hit here!);
    
Broadcast::channel('channel_for_everyone', function ($user) {
  dd('callback hit for channel_for_everyone', $user); // Will not hit
  return true;
});

My React code is super basic, I made a hook to execute and it logs a successful connection, just no events are every logged.

const useLaravelEcho = () => {
    useEffect(() => {
        console.log('Initialising Echo...');

        const pusher = Pusher; // we need to make an instance

        const echoConfig = new Echo({
            broadcaster: 'reverb',
            key: import.meta.env.VITE_REVERB_APP_KEY,
            wsHost: import.meta.env.VITE_REVERB_HOST,
            wsPort: import.meta.env.VITE_REVERB_PORT,
            forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
            enabledTransports: ['ws', 'wss'],
            authEndpoint: import.meta.env.VITE_AUTH_ENDPOINT,
        });

        // Listen for successful connection
        echoConfig.connector.pusher.connection.bind('connected', () => {
            console.log('Successfully connected to Echo server.');
        });

        // Listen for connection errors
        echoConfig.connector.pusher.connection.bind('error', (error: any) => {
            console.error('Error connecting to Echo server:', error);
        });

        console.log('Subscribing to channel...');
        echoConfig.private('channel_for_everyone')
            .listen('CommentCreatedEvent', (event: any) => {
                console.log('Event received:', event);
            });
    }, []);
};

export default useLaravelEcho;

When I check the console in browser:

Initialising Echo...
useLaravelEcho.ts:32 Subscribing to channel...
useLaravelEcho.ts:24 Successfully connected to Echo server.

I know this must be some kind of configuration error but I just cannot seem to find the issue as I have no errors or logs to go off!

Has anyone got any idea?

3
  • Have you edited the allowed_origins on the reverb config file, in order to add your host, or you may allow all origins using *? Commented Jun 21, 2024 at 8:54
  • what did you get if you run this command php artisan channel:list? Commented Jun 21, 2024 at 9:22
  • I have * set in the allowed_origins and I have my channel_for_everyone showing when running php artisan channel:list Commented Jun 24, 2024 at 7:20

1 Answer 1

0

Fixed! There were a few issues in the end.

Backend REST api:

  1. broadcasting.php ensuring that the BROADCAST_DRIVER is set to reverb as well as the BROADCAST_CONNECTION is set to reverb:

This wasn't clear and the default options in the comments Supported: "pusher", "ably", "redis", "log", "null" do not state reverb as an option. I thought setting null would ignore this and my reverb config file would control this.

  1. I am dealing with JWT tokens to authenticate my users within the app and I needed to make a custom endpoint to handle this for me due to the nature of my setup.

Code:

public function reverb(ReverbRequest $request): Response
{
    $socketId = $request->input('socket_id');
    $channelName = $request->input('channel_name');

    // this generates the required format for the response
    $stringToAuth = $socketId . ':' . $channelName;
    $hashed = hash_hmac('sha256', $stringToAuth, env('REVERB_APP_SECRET'));
    
    try {
        // Generate the required format for the response
        $stringToAuth = $socketId . ':' . $channelName;
        $hashed = hash_hmac('sha256', $stringToAuth, env('REVERB_APP_SECRET'));

        return response(['auth' => env('REVERB_APP_KEY') . ':' . $hashed]);
    } catch (Exception $e) {
        return response(['code' => 403, 'message' => 'Cannot authenticate reverb'], 403);
    }
}

Front end

  1. Pass the bearer token into the call to authenticate the user. I have also added a few improvements to the code here to.

Code:

const useLaravelEcho = () => {
    const [{ user }] = useUserContext();
    const { showNotificationToast } = useShowToast();
    const echoInstance = useRef<Echo | null>(null);

    useEffect(() => {
        if (!user) return;

        // Ensure only one Echo instance is created
        if (!echoInstance.current) {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const pusher = Pusher; // Needed for the Echo configuration to work

            echoInstance.current = new Echo({
                broadcaster: 'reverb',
                key: import.meta.env.VITE_REVERB_APP_KEY,
                wsHost: import.meta.env.VITE_REVERB_HOST,
                wsPort: import.meta.env.VITE_REVERB_PORT,
                forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
                enabledTransports: ['ws', 'wss'],
                authEndpoint: import.meta.env.VITE_AUTH_ENDPOINT,
                auth: {
                    headers: {
                        'Authorization': 'Bearer ' + user?.access_token
                    }
                }
            });

            // Listen for successful connection
            echoInstance.current.connector.pusher.connection.bind('connected', () => {
                console.log('Successfully connected to Echo server.');
            });

            // Listen for connection errors
            echoInstance.current.connector.pusher.connection.bind('error', (error: any) => {
                console.error('Error connecting to Echo server:', error);
            });
        }

        const channel = echoInstance.current.private('channel_for_everyone')
            .listen(CommentCreatedEvent', (event: any) => {
                showNotificationToast({text: `You have received a new message from ${event?.user?.name}`);
            });

        // Cleanup function to remove listeners and Echo instance on unmount or dependency change
        return () => {
            if (echoInstance.current) {
                channel.stopListening('CommentCreatedEvent');
                echoInstance.current.disconnect();
                echoInstance.current = null;
            }
        };
    }, [user, showNotificationToast]);

    return null;
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.