laravel-websocket
Auto-invoked skill
Build real-time features with Laravel Reverb
Trigger Keywords
This skill automatically activates when Claude detects these keywords:
websocket
real-time
Reverb
broadcast
Echo
pusher
Overview
The laravel-websocket skill provides expertise for building real-time features with Laravel Reverb. It covers WebSocket setup, broadcasting events, presence channels, and client-side integration with Laravel Echo.
What This Skill Provides
- Reverb Setup - First-party WebSocket server configuration
- Broadcasting - Event broadcasting to channels
- Channel Types - Public, private, and presence channels
- Laravel Echo - Client-side WebSocket integration
- Scaling - Redis pub/sub for horizontal scaling
- Authentication - Private channel authorization
Example Conversations
# Setting up Reverb
"Set up Laravel Reverb for real-time notifications"
# Broadcasting events
"Create a broadcast event when a new message is sent"
# Presence channels
"Show who's online in a chat room using presence channels"
# Client integration
"How do I listen for events with Laravel Echo?"
Broadcast Event Example
<?php
namespace App\Events;
use App\Models\Message;
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 MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public Message $message
) {}
public function broadcastOn(): array
{
return [
new PrivateChannel('chat.' . $this->message->conversation_id),
];
}
public function broadcastWith(): array
{
return [
'id' => $this->message->id,
'body' => $this->message->body,
'user' => $this->message->user->only('id', 'name', 'avatar'),
'created_at' => $this->message->created_at->toISOString(),
];
}
public function broadcastAs(): string
{
return 'message.sent';
}
}
Channel Authorization
// routes/channels.php
// Private channel - user can only access their own
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return $user->id === Order::find($orderId)?->user_id;
});
// Presence channel - returns user data for "who's here"
Broadcast::channel('chat.{conversationId}', function ($user, $conversationId) {
if ($user->canAccessConversation($conversationId)) {
return [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar_url,
];
}
});
Laravel Echo Client
// resources/js/bootstrap.js
import 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') === 'https',
enabledTransports: ['ws', 'wss'],
});
// Listening to events
Echo.private(`chat.${conversationId}`)
.listen('.message.sent', (event) => {
console.log('New message:', event);
messages.push(event);
});
// Presence channel - track who's online
Echo.join(`chat.${conversationId}`)
.here((users) => {
onlineUsers = users;
})
.joining((user) => {
onlineUsers.push(user);
})
.leaving((user) => {
onlineUsers = onlineUsers.filter(u => u.id !== user.id);
});
Channel Types
| Type | Use Case | Auth Required |
|---|---|---|
Channel |
Public data (scores, prices) | No |
PrivateChannel |
User-specific data (notifications) | Yes |
PresenceChannel |
Who's online (chat rooms) | Yes + user data |
Common Pitfalls
- Missing queue worker - Broadcasts require
php artisan queue:work - CORS issues - Configure
config/cors.phpfor WebSocket connections - Auth endpoint - Ensure
/broadcasting/authroute exists - Event naming - Use
broadcastAs()for consistent event names
Scaling with Redis
// config/broadcasting.php
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'options' => [
'host' => env('REVERB_HOST'),
'port' => env('REVERB_PORT', 443),
'scheme' => env('REVERB_SCHEME', 'https'),
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
],
'client_options' => [],
],
// For horizontal scaling, use Redis
// .env
BROADCAST_DRIVER=reverb
REVERB_SCALING_ENABLED=true
Testing WebSockets
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Broadcast;
it('broadcasts message sent event', function () {
Event::fake();
$message = Message::factory()->create();
event(new MessageSent($message));
Event::assertDispatched(MessageSent::class, function ($event) use ($message) {
return $event->message->id === $message->id;
});
});
it('authorizes private channel access', function () {
$user = User::factory()->create();
$conversation = Conversation::factory()->create();
$conversation->users()->attach($user);
$this->actingAs($user)
->post('/broadcasting/auth', [
'channel_name' => "private-chat.{$conversation->id}",
])
->assertOk();
});
Related Commands
- /laravel-agent:reverb:setup - Setup Laravel Reverb
- /laravel-agent:broadcast:make - Create broadcast events
Related Agent
- laravel-reverb - WebSocket specialist