Web Development

Building Real-Time Dashboards with Laravel and WebSockets

December 11, 2024 5 min read By Amey Lokare

Building Real-Time Dashboards with Laravel and WebSockets

Real-time dashboards are essential for monitoring systems, tracking live data, and providing instant feedback. I've built several production dashboards using Laravel, WebSockets, and modern JavaScript to create responsive, live-updating interfaces.

🎯 Why Real-Time Dashboards?

Modern applications need:

  • Live system monitoring (server stats, call queues)
  • Instant notifications (alerts, updates)
  • Collaborative features (live editing, presence)
  • Live data visualization (charts, metrics)
Traditional polling is inefficient. WebSockets provide bidirectional, low-latency communication.

🏗 Architecture

``` Laravel Backend → Laravel Echo Server → WebSocket → JavaScript Client ↓ Events/Broadcasting ```

Components

1. Laravel Events: Trigger updates 2. Broadcasting: Send to WebSocket server 3. Laravel Echo Server: WebSocket server (or Pusher) 4. Frontend: JavaScript client receives updates

💻 Implementation

1. Install Dependencies

```bash

Laravel Broadcasting

composer require pusher/pusher-php-server

Or use Laravel Echo Server (self-hosted)

npm install -g laravel-echo-server

Frontend

npm install laravel-echo pusher-js ```

2. Configure Broadcasting

```php // config/broadcasting.php 'connections' => [ 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'cluster' => env('PUSHER_APP_CLUSTER'), 'encrypted' => true, ], ],

// Or use Laravel Echo Server 'redis' => [ 'driver' => 'redis', 'connection' => 'default', ], ], ```

3. Create Events

```php // app/Events/CallStatusUpdated.php namespace App\Events;

use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Queue\SerializesModels;

class CallStatusUpdated implements ShouldBroadcast { use InteractsWithSockets, SerializesModels;

public $call; public $status;

public function __construct($call, $status) { $this->call = $call; $this->status = $status; }

public function broadcastOn() { return new Channel('calls'); }

public function broadcastAs() { return 'status.updated'; }

public function broadcastWith() { return [ 'call_id' => $this->call->id, 'status' => $this->status, 'timestamp' => now()->toIso8601String(), ]; } } ```

4. Trigger Events

```php // In your controller or service use App\Events\CallStatusUpdated;

// When call status changes event(new CallStatusUpdated($call, 'answered'));

// Or broadcast to specific user broadcast(new CallStatusUpdated($call, 'answered')) ->toOthers(); ```

5. Frontend: Listen for Events

```javascript // resources/js/app.js import Echo from 'laravel-echo'; import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({ broadcaster: 'pusher', key: process.env.MIX_PUSHER_APP_KEY, cluster: process.env.MIX_PUSHER_APP_CLUSTER, encrypted: true, });

// Listen to channel Echo.channel('calls') .listen('.status.updated', (e) => { console.log('Call status updated:', e); updateCallStatus(e.call_id, e.status); }); ```

📊 Dashboard Example: Live Call Monitor

Backend: Call Monitoring

```php // app/Http/Controllers/CallMonitorController.php class CallMonitorController extends Controller { public function index() { return view('dashboard.calls'); }

public function getActiveCalls() { $calls = Call::where('status', 'active')->get(); return response()->json($calls); } }

// When call status changes public function updateCallStatus($callId, $status) { $call = Call::find($callId); $call->status = $status; $call->save();

// Broadcast update broadcast(new CallStatusUpdated($call, $status))->toOthers(); } ```

Frontend: Live Dashboard

```javascript // resources/js/dashboard.js class CallDashboard { constructor() { this.calls = []; this.init(); }

init() { // Load initial data this.loadCalls();

// Listen for updates Echo.channel('calls') .listen('.status.updated', (e) => { this.updateCall(e.call_id, e.status); }) .listen('.call.started', (e) => { this.addCall(e.call); }) .listen('.call.ended', (e) => { this.removeCall(e.call_id); }); }

async loadCalls() { const response = await fetch('/api/calls/active'); this.calls = await response.json(); this.render(); }

updateCall(callId, status) { const call = this.calls.find(c => c.id === callId); if (call) { call.status = status; this.render(); } }

render() { const container = document.getElementById('calls-container'); container.innerHTML = this.calls.map(call => `

${call.from} → ${call.to}

${call.status}

Duration: ${this.formatDuration(call.duration)}

`).join(''); } }

new CallDashboard(); ```

🔐 Private Channels & Authentication

For user-specific data:

```php // app/Events/UserNotification.php public function broadcastOn() { return new PrivateChannel('user.' . $this->userId); }

// Frontend Echo.private(`user.${userId}`) .listen('.notification', (e) => { showNotification(e.message); }); ```

Authentication

```php // routes/channels.php Broadcast::channel('user.{userId}', function ($user, $userId) { return (int) $user->id === (int) $userId; }); ```

📈 Real-Time Charts

Update charts in real-time:

```javascript // Using Chart.js import Chart from 'chart.js/auto';

const chart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'Active Calls', data: [], }] } });

// Update on new data Echo.channel('metrics') .listen('.metric.updated', (e) => { chart.data.labels.push(e.timestamp); chart.data.datasets[0].data.push(e.value);

// Keep only last 50 points if (chart.data.labels.length > 50) { chart.data.labels.shift(); chart.data.datasets[0].data.shift(); }

chart.update(); }); ```

🚀 Performance Optimization

1. Throttling Events

Don't broadcast every change:

```php // Throttle to once per second Cache::remember("last_broadcast_{$callId}", 1, function() { broadcast(new CallStatusUpdated($call, $status)); return true; }); ```

2. Batch Updates

Send multiple updates at once:

```php class BatchUpdate implements ShouldBroadcast { public $updates;

public function broadcastWith() { return [ 'updates' => $this->updates, // Array of changes ]; } } ```

3. Conditional Broadcasting

Only broadcast when needed:

```php if ($call->status !== $oldStatus) { broadcast(new CallStatusUpdated($call, $status)); } ```

💡 Real-World Example

I built a VoIP call monitoring dashboard:

1. Live Call List: Shows all active calls 2. Real-Time Stats: Call count, duration, queue length 3. Agent Status: Who's available/busy 4. Call Details: Real-time updates as calls progress 5. Alerts: Notifications for errors or issues

Result: Sub-100ms update latency, handles 1000+ concurrent connections.

🎓 Key Takeaways

  • Laravel Events make broadcasting easy
  • WebSockets provide low-latency updates
  • Private channels for user-specific data
  • Throttle to prevent overload
  • Batch updates for efficiency

Conclusion

Real-time dashboards transform user experience by providing instant feedback. Laravel's broadcasting system combined with WebSockets creates a powerful foundation for live applications. Start simple, measure performance, and optimize based on your specific needs.

Comments

Leave a Comment

Related Posts