Agent skill
realtime
Implementa WebSockets com Laravel Reverb para funcionalidades em tempo real. Use quando precisar adicionar WebSockets, broadcasting, eventos em tempo real, ou notificações push.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/realtime
Metadata
Additional technical details for this skill
- author
- aronpc
- version
- 1.0.0
- category
- laravel
SKILL.md
realtime
Resumo
Implementa WebSockets com Laravel Reverb para comunicação em tempo real.
Skills Relacionadas
| Skill | Quando usar junto |
|---|---|
actions |
Para broadcasting de events |
testing |
Para testar WebSockets |
mcp |
Para validar realtime no browser |
cicd |
Para deploy com Reverb |
Quando usar
Use esta skill sempre que:
- Implementar WebSockets com Laravel Reverb
- Criar canais presence/private/public
- Fazer broadcasting de events
- Configurar autenticação de canais
- Integrar com frontend React
- Criar notificações em tempo real
- Debugar conexões WebSocket
Stack Tecnológico
Backend: Laravel Reverb (WebSockets)
Frontend: React + Echo ou Browser WebSocket API
Instalação
Instalar Laravel Reverb
composer require laravel/reverb
php artisan reverb:install
npm install laravel-echo pusher-js
Configurar Reverb
# .env
REVERB_APP_ID=local-app
REVERB_APP_KEY=local-key
REVERB_APP_SECRET=local-secret
REVERB_HOST=localhost
REVERB_PORT=8081
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
Broadcasting Events
Criar Event
php artisan make:event OrderStatusChanged
<?php
declare(strict_types=1);
namespace App\Events;
use App\Models\Order;
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;
final class OrderStatusChanged implements ShouldBroadcast
{
use Dispatchable;
use InteractsWithSockets;
public function __construct(
public readonly Order $order,
public readonly string $oldStatus,
public readonly string $newStatus,
) {}
/**
* Canal público.
*/
public function broadcastOn(): Channel
{
return new Channel('orders');
}
/**
* Canal privado.
*/
public function broadcastOn(): PrivateChannel
{
return new PrivateChannel('orders.' . $this->order->id);
}
/**
* Canal presence.
*/
public function broadcastOn(): PresenceChannel
{
return new PresenceChannel('orders.' . $this->order->id);
}
}
Dispatch Event
// Em Actions ou Controllers
event(new OrderStatusChanged($order, $oldStatus, $newStatus));
// Ou com delay
event(new OrderStatusChanged($order, $oldStatus, $newStatus))->delay(now()->addSeconds(5));
Tipos de Canais
Public Channel
// Event
public function broadcastOn(): Channel
{
return new Channel('public-updates');
}
// Autorização: Nenhuma
// Frontend: Echo.channel('public-updates')
Private Channel
// Event
public function broadcastOn(): PrivateChannel
{
return new PrivateChannel('orders.' . $this->order->id);
}
// Autorização: Required
// Frontend: Echo.private('orders.' . orderId)
// routes/channels.php
Broadcast::channel('orders.{order}', function ($user, Order $order) {
return $user->tenant_id === $order->tenant_id;
});
Presence Channel
// Event
public function broadcastOn(): PresenceChannel
{
return new PresenceChannel('tracking.{orderId}');
}
// Autorização: Required
// Frontend: Echo.join('tracking.' + orderId)
// routes/channels.php
Broadcast::channel('tracking.{orderId}', function ($user, $orderId) {
$order = Order::find($orderId);
return $order && $user->tenant_id === $order->tenant_id;
});
Autenticação de Canais
Configurar Channels
<?php
// routes/channels.php
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
// Private channel - acesso por usuário
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
return $user->tenant_id === $order->tenant_id;
});
// Private channel - acesso por tenant
Broadcast::channel('tenant.{tenantId}', function (User $user, int $tenantId) {
return $user->tenant_id === $tenantId;
});
// Presence channel - tracking em tempo real
Broadcast::channel('tracking.{orderId}', function (User $user, int $orderId) {
$order = Order::find($orderId);
return $order && $user->tenant_id === $order->tenant_id
&& $user->can('view', $order);
});
Channel Classes
<?php
namespace App\Broadcasting;
use App\Models\Order;
use App\Models\User;
final class OrderChannel
{
public function join(User $user, Order $order): bool
{
return $user->tenant_id === $order->tenant_id
&& $user->can('view', $order);
}
}
// routes/channels.php
Broadcast::channel('orders.{order}', OrderChannel::class);
Frontend React
Configurar Echo
// resources/js/echo.ts
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 ?? 8081,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 8081,
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
Ouvir Eventos
// Public channel
window.Echo.channel('public-updates')
.listen('.OrderStatusChanged', (e: any) => {
console.log('Order status changed:', e.order);
});
// Private channel
window.Echo.private(`orders.${orderId}`)
.listen('.OrderStatusChanged', (e: any) => {
console.log('Order updated:', e.order);
// Atualizar estado React
setOrder(e.order);
});
// Presence channel - tracking
window.Echo.join(`tracking.${orderId}`)
.here((users: any[]) => {
console.log('Users watching:', users);
})
.joining((user: any) => {
console.log('User joined:', user);
// Mostrar notificação
toast(`${user.name} está acompanhando este pedido`);
})
.leaving((user: any) => {
console.log('User left:', user);
})
.error((error: any) => {
console.error('Presence error:', error);
});
// Notification channel
window.Echo.private(`App.Models.User.${userId}`)
.notification((notification: any) => {
console.log('New notification:', notification);
// Mostrar toast
toast(notification.message);
});
React Hook
// resources/js/hooks/useEcho.ts
import { useEffect, useState } from 'react';
import window from 'global';
interface UseEchoOptions {
channel: string;
event: string;
type?: 'public' | 'private' | 'presence';
}
export function useEcho({ channel, event, type = 'public' }: UseEchoOptions) {
const [data, setData] = useState<any>(null);
useEffect(() => {
const echo = (window as any).Echo;
if (!echo) return;
let echoChannel;
switch (type) {
case 'private':
echoChannel = echo.private(channel);
break;
case 'presence':
echoChannel = echo.join(channel);
break;
default:
echoChannel = echo.channel(channel);
}
const listener = echoChannel.listen(event, (e: any) => {
setData(e);
});
return () => {
listener.stopListening(channel, event);
};
}, [channel, event, type]);
return data;
}
// Uso
function OrderTracking({ orderId }: { orderId: number }) {
const update = useEcho({
channel: `orders.${orderId}`,
event: '.OrderStatusChanged',
type: 'private',
});
useEffect(() => {
if (update) {
// Atualizar UI
}
}, [update]);
return <div>...</div>;
}
Client Notifications
Backend
<?php
use App\Notifications\OrderUpdated;
use Illuminate\Support\Facades\Notification;
// Enviar notification em tempo real
Notification::send($users, new OrderUpdated($order));
Frontend
// Ouvir notifications do usuário atual
window.Echo.private(`App.Models.User.${userId}`)
.notification((notification: any) => {
console.log('Notification:', notification);
// Mostrar toast/badge
});
Debugging WebSockets
Verificar Conexão
// Verificar status da conexão
window.Echo.connector.pusher.connection.bind('connected', () => {
console.log('WebSocket connected');
});
window.Echo.connector.pusher.connection.bind('disconnected', () => {
console.log('WebSocket disconnected');
});
window.Echo.connector.pusher.connection.bind('error', (error: any) => {
console.error('WebSocket error:', error);
});
Debug Events
// config/broadcasting.php
'default' => env('BROADCAST_CONNECTION', 'reverb'),
'connections' => [
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'url' => env('REVERB_URL'),
'host' => env('REVERB_HOST', '127.0.0.1'),
'port' => env('REVERB_PORT', 8081),
'scheme' => env('REVERB_SCHEME', 'http'),
'options' => [
'cluster' => env('REVERB_CLUSTER', 'mt1'),
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
'client' => env('REVERB_CLIENT', 'laravel-echo'),
'debug' => env('REVERB_DEBUG', false), // Enable for debug
],
],
],
Casos de Uso
Rastreamento em Tempo Real
// Event: TrackingUpdated
public function broadcastOn(): PresenceChannel
{
return new PresenceChannel('tracking.' . $this->order->id);
}
// Frontend
window.Echo.join(`tracking.${orderId}`)
.here((users) => setViewers(users))
.joining((user) => toast(`${user.name} está observando`))
.leaving((user) => removeViewer(user.id));
Atualizações de Status
// Action: UpdateOrderStatusAction
public function handle(Order $order, string $status): Order
{
$oldStatus = $order->status;
$order->update(['status' => $status]);
event(new OrderStatusChanged($order, $oldStatus, $status));
return $order;
}
// Frontend
window.Echo.private(`orders.${orderId}`)
.listen('.OrderStatusChanged', (e) => {
setOrderStatus(e.order.status);
toast(__('messages.order_status_updated'));
});
Chat em Tempo Real
// Event: NewMessage
public function broadcastOn(): PresenceChannel
{
return new PresenceChannel('chat.' . $this->chat->id);
}
// Frontend
window.Echo.join(`chat.${chatId}`)
.listen('.NewMessage', (e) => {
addMessage(e.message);
});
Performance
Rate Limiting
// Limitar broadcasts por usuário
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('broadcast', function (Job $job) {
return Limit::perMinute(100);
});
Queue Broadcasting
// Events podem ser queued
final class LargeDataEvent implements ShouldBroadcast
{
use InteractsWithSockets;
use SerializesModels;
public $queue = 'broadcast'; // Fila específica
public $connection = 'redis';
// ...
}
Segurança
Validar Autorização
// Sempre validar canais private/presence
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
// Validar tenant ownership
if ($user->tenant_id !== $order->tenant_id) {
return false;
}
// Validar permissões
return $user->can('view', $order);
});
Sanitizar Dados
// Não enviar dados sensíveis
public function broadcastWith(): array
{
return [
'id' => $this->order->id,
'status' => $this->order->status,
// Não enviar: user_id, tenant_id, etc.
];
}
Melhores Práticas
✅ FAÇA
- Use canais private/presence para dados sensíveis
- Sempre validar autorização de canais
- Use filas para eventos grandes
- Sanitizar dados enviados
- Testar desconexões e reconexões
- Implementar rate limiting
- Usar presence channels para contagem de usuários
- Tratar erros de WebSocket
- Desconectar canais quando não necessário
- Log eventos para debugging
❌ NÃO FAÇA
- Não envie dados sensíveis via WebSocket
- Não ignore autorização de canais
- Não use canais públicos para dados privados
- Não esqueça de desconectar canais
- Não implemente lógica de negócio em frontend
- Não abuse de broadcasts (rate limit)
- Não envie payloads muito grandes
- Não dependa apenas de WebSocket (fallback)
Checklist de WebSocket
Antes de finalizar feature realtime:
- Reverb configurado e funcionando
- Canais criados (public/private/presence)
- Autorização de canais implementada
- Events com broadcasting criados
- Frontend Echo configurado
- Listeners de events criados
- Presence channels testados
- Desconexões tratadas
- Rate limiting configurado
- Testes de integração criados
Referências Cruzadas
- Actions: Use com
laravel-actions-eventspara dispatch events - Exceptions: Trate erros de WebSocket com
laravel-exceptions - i18n: Traduza mensagens realtime com
laravel-i18n - Architecture: Integra com
laravel-architecturepara estrutura
Referências
- Laravel Reverb Documentation - Documentação oficial
- Laravel Broadcasting - Broadcasting de events
- Laravel Echo Documentation - Cliente frontend
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?