Fitur real-time seperti notifikasi instan, chat, atau dashboard yang update otomatis dulu membutuhkan layanan pihak ketiga berbayar seperti Pusher atau Ably. Sejak Laravel 11, hadir Laravel Reverb — WebSocket server yang ditulis dalam PHP, dibangun langsung ke ekosistem Laravel, dan bisa self-hosted gratis. Artikel ini membahas cara implementasinya dari awal.
Arsitektur Real-time di Laravel
Sebelum masuk ke implementasi, penting memahami alur data real-time di Laravel:
- Event di-dispatch dari aplikasi Laravel (controller, job, model)
- Event di-broadcast ke channel WebSocket via Reverb
- Frontend (JavaScript) listen ke channel dan update UI
Instalasi Laravel Reverb
php artisan install:broadcasting
# Ini akan menginstall:
# - laravel/reverb
# - laravel-echo
# - pusher-js (digunakan oleh Echo sebagai WebSocket client)
# .env
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http
# Untuk frontend
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
Membuat Broadcastable Event
php artisan make:event PostPublished
class PostPublished implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public readonly Post $post) {}
/**
* Channel yang menerima event ini.
*/
public function broadcastOn(): array
{
return [
new PublicChannel('posts'), // Semua user bisa dengar
new PrivateChannel("user.{$this->post->author_id}"), // Hanya author
];
}
/**
* Data yang dikirim ke frontend.
*/
public function broadcastWith(): array
{
return [
'id' => $this->post->id,
'title' => $this->post->title,
'slug' => $this->post->slug,
'url' => route('posts.show', $this->post),
];
}
/**
* Nama event di frontend (default: nama class).
*/
public function broadcastAs(): string
{
return 'post.published';
}
}
Channel Authorization untuk Private Channel
// routes/channels.php
Broadcast::channel('user.{id}', function (User $user, int $id) {
return $user->id === $id;
});
// Channel untuk admin saja
Broadcast::channel('admin.dashboard', function (User $user) {
return $user->hasRole('admin');
});
Dispatch Event
// Di mana saja di aplikasi — controller, job, observer
PostPublished::dispatch($post);
// Atau dengan delay
PostPublished::dispatch($post)->delay(now()->addSeconds(5));
// Broadcast tanpa queue (langsung)
broadcast(new PostPublished($post));
Frontend: Listen dengan Laravel Echo
// resources/js/bootstrap.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 ?? 80,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
// Listen ke public channel
Echo.channel('posts')
.listen('.post.published', (data) => {
console.log('Post baru:', data.title);
showToast(`Post baru: ${data.title}`);
});
// Listen ke private channel (perlu autentikasi)
Echo.private(`user.${userId}`)
.listen('.post.published', (data) => {
addNotification({ message: `Post Anda "${data.title}" dipublikasikan!` });
});
// Presence channel (tahu siapa yang online)
Echo.join('chat.room.1')
.here((users) => { onlineUsers.value = users; })
.joining((user) => { onlineUsers.value.push(user); })
.leaving((user) => { onlineUsers.value = onlineUsers.value.filter(u => u.id !== user.id); })
.listen('.message.sent', (data) => { messages.value.push(data); });
Studi Kasus: Notifikasi Real-time
// app/Notifications/NewCommentNotification.php
class NewCommentNotification extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(private readonly Comment $comment) {}
public function via(object $notifiable): array
{
return ['database', 'broadcast'];
}
public function toBroadcast(object $notifiable): BroadcastMessage
{
return new BroadcastMessage([
'id' => $this->comment->id,
'message' => "Komentar baru di: {$this->comment->post->title}",
'author' => $this->comment->author->name,
'created_at' => $this->comment->created_at->diffForHumans(),
]);
}
}
// Kirim notifikasi
$post->author->notify(new NewCommentNotification($comment));
Menjalankan Reverb Server
# Development
php artisan reverb:start
# Production dengan opsi
php artisan reverb:start --host="0.0.0.0" --port=8080 --env=production
# Dengan Supervisor (production)
# /etc/supervisor/conf.d/reverb.conf
[program:reverb]
command=php /var/www/app/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
user=www-data
Performance dan Scaling
Reverb menggunakan ReactPHP untuk event-driven I/O yang efisien. Satu instance bisa menangani ribuan koneksi concurrent. Untuk aplikasi besar, Reverb mendukung horizontal scaling via Redis pub/sub:
# config/reverb.php
'scaling' => [
'driver' => 'redis',
'connection' => 'default',
],
Dengan Reverb, fitur real-time yang dulu memerlukan biaya Pusher $49/bulan ke atas kini bisa dijalankan sendiri. Untuk aplikasi dengan traffic sedang hingga tinggi, self-hosting Reverb di VPS yang sama dengan Laravel bisa menghemat biaya signifikan.