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?
Fixed! There were a few issues in the end.
Backend REST api:
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.
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
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;
};