Laravel Echo / Reverb
Socket Conveyor can act as a Pusher/Reverb-compatible WebSocket and REST broadcast server, while Laravel keeps using its built-in broadcasting tools.
1Start Conveyor in Pusher mode
<?php
use Conveyor\Constants;
use Conveyor\ConveyorServer;
require __DIR__ . '/vendor/autoload.php';
(new ConveyorServer())
->host('127.0.0.1')
->port(8990)
->serverOptions([
'worker_num' => 1,
'task_worker_num' => 1,
])
->conveyorOptions([
Constants::WEBSOCKET_SUBPROTOCOL => Constants::PUSHER,
Constants::USE_PRESENCE => true,
Constants::APPS => [[
'app_id' => 'local-app',
'key' => 'local-key',
'secret' => 'local-secret',
'enable_client_messages' => true,
'enabled' => true,
]],
])
->start();The repository includes the same setup for local smoke testing:
php examples/pusher-real/run-conveyor.php2Configure Laravel
Use Reverb-style environment variables and point them at Conveyor, then mirror them into VITE_REVERB_* for the frontend:
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=local-app
REVERB_APP_KEY=local-key
REVERB_APP_SECRET=local-secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8990
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"If you use Laravel's pusher connection, use the equivalent PUSHER_* values.
Install broadcasting
Recent Laravel versions do not enable broadcasting by default. install:broadcasting prompts for a service; --reverb targets Reverb directly.
php artisan install:broadcastingphp artisan install:broadcasting --reverbThe command creates or ensures config/broadcasting.php, routes/channels.php, and the required environment variables and bootstrap wiring. On older Laravel versions or custom setups, publish the broadcasting routes and config manually.
Confirm the broadcasting.auth route is registered:
php artisan route:list --name=broadcastingTested versions: this static-site handoff does not record exact tested Laravel, Laravel Echo, pusher-js, PHP, OpenSwoole or Conveyor versions. These examples are configuration guidance; verify your installed combination using the browser smoke test below. See tested-version status.
3Configure Echo
npm install laravel-echo pusher-jsimport Echo from 'laravel-echo'
import Pusher from 'pusher-js'
window.Pusher = Pusher
window.Echo = 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,
wssPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: import.meta.env.VITE_REVERB_SCHEME === 'https',
enabledTransports: ['ws', 'wss'],
})Private and presence channels
Keep the WebSocket host on Conveyor and set authEndpoint to your Laravel app's /broadcasting/auth. auth.withCredentials sends session credentials.
VITE_LARAVEL_URL=http://localhost:8000window.Echo = 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,
wssPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: import.meta.env.VITE_REVERB_SCHEME === 'https',
enabledTransports: ['ws', 'wss'],
authEndpoint: `${import.meta.env.VITE_LARAVEL_URL}/broadcasting/auth`,
auth: {
withCredentials: true,
},
})4Use Echo normally
window.Echo.channel('orders')
.listen('.OrderShipped', event => {
console.log(event)
})
window.Echo.private('orders.1')
.listen('.OrderUpdated', event => {
console.log(event)
})
window.Echo.join('room.1')
.here(users => console.log('here', users))
// ...Presence methods — .here(), .joining(), .leaving(), .listenForWhisper() — and whispers are covered on Presence.
Define channel authorization in routes/channels.php. Private callbacks return true only when access is allowed; presence callbacks return user data only for authorized members. Return false to deny access.
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('orders.{orderId}', function ($user, int $orderId) {
return $user->orders()->whereKey($orderId)->exists();
});
Broadcast::channel('room.{roomId}', function ($user, int $roomId) {
if (! $user->rooms()->whereKey($roomId)->exists()) {
return false;
}
return [
'id' => $user->id,
'name' => $user->name,
];
});Signing in alone does not grant channel access. This example assumes orders() contains orders owned by the user and rooms() contains rooms they belong to. Adapt these relationships or use your application’s authorization policies. A user requesting another user’s order, or a room they have not joined, is denied access; presence user data is returned only after membership is confirmed.
Broadcast events normally, and chain toOthers() to exclude the sender:
broadcast(new OrderShipped($order))->toOthers();Conveyor receives Laravel's signed Pusher/Reverb REST publish request at /apps/{app_id}/events, delivers it to Echo clients, and honors socket_id exclusion for toOthers().
Verify with the browser smoke
php examples/pusher-real/run-conveyor.phpphp -S 127.0.0.1:8991 examples/pusher-real/router.phpOpen:
http://127.0.0.1:8991/echo.htmlExpected:
- The page reaches
connectedand shows asocket_id. - Public, private, and presence subscriptions succeed.
- The Public, Private, and Presence buttons log
DemoEvent. Public toOthers()does not echo to the current tab.- A second tab receives a whisper from the first.
About conveyor-laravel-broadcaster
Pusher/Reverb-compatible Echo usage does not need the native Conveyor Laravel broadcaster. Use Laravel's built-in reverb or pusher driver and point it at Conveyor.
For applications using Conveyor’s native HTTP endpoints and native clients, see the separate Laravel native broadcaster guide.