ably/laravel-broadcaster
Official Ably broadcaster for Laravel. Send broadcast events over Ably’s realtime messaging platform with support for public, private, and presence channels, token authentication, and seamless integration with Laravel’s broadcasting system.
What it is: ably/laravel-broadcaster adds Ably as a first-class broadcast driver for Laravel. It is a drop-in alternative to Pusher/Reverb — you keep ShouldBroadcast events, broadcastOn(), routes/channels.php authorization, and Echo on the frontend; only the transport changes. Ably handles the WebSocket fan-out (and global edge delivery) so you don't run a socket server.
Requirements (as of v1.0.8): PHP 8.x, illuminate/* v10–v13, and ably/ably-php. v1.0.8 is a compatibility release — it adds Laravel 13 support (PR #68) with no API changes, so upgrading from 1.0.7 is a composer constraint bump only. If you're planning a Laravel 13 upgrade, this is the version that unblocks it.
Minimal setup:
composer require ably/laravel-broadcaster
php artisan install:broadcasting # Laravel 11+: scaffolds config/broadcasting.php + routes/channels.php
Add the connection manually — the installer only scaffolds pusher/reverb/log/null:
// config/broadcasting.php
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
BROADCAST_CONNECTION=ably # Laravel 11+ (BROADCAST_DRIVER=ably on 10.x)
ABLY_KEY=xxxxxxxx.yyyyyy:zzzzzzzzzzzzzzzz
The key is the full Ably API key (appId.keyName:secret) from the Ably dashboard, not just the app ID or a public token — this is the most common first-run error.
Where to look first: the package README for the exact Echo client snippet, then Ably's dashboard Dev Console to watch messages arrive live. Your first use case should be a single private channel (orders.{id}) with an authorization callback in routes/channels.php, broadcast from one event, subscribed to in one Blade/Inertia page. Prove the whole loop end-to-end before wiring more channels.
Standard event broadcast. Nothing Ably-specific:
class OrderShipped implements ShouldBroadcast
{
public function __construct(public Order $order) {}
public function broadcastOn(): array
{
return [new PrivateChannel('orders.'.$this->order->user_id)];
}
}
Dispatch normally (OrderShipped::dispatch($order)). If the queue connection is sync, the HTTP call to Ably happens in-request — set ShouldBroadcast (queued) rather than ShouldBroadcastNow in production.
Frontend. Install ably alongside laravel-echo and point Echo at the ably connector with /broadcasting/auth as the auth URL — this is the endpoint your routes/channels.php callbacks protect, and it's the entire security boundary. Confirm the connector name against the README for your laravel-echo version, as Echo's client connectors vary by release; if the built-in connector isn't available, instantiate Ably's JS client directly and subscribe to the same channel names (private-orders.42, presence-room.7). Note that install:broadcasting pulls in pusher-js — replace it, don't ship both.
Presence and notifications. Presence channels work through the same private/presence naming, so join/here/leaving on the Ably side map onto the usual Echo presence API. For Laravel notifications, broadcast() on a Notification routes through the same connection — no per-notification config needed.
Multi-tenant / multi-region. Ably is a single global endpoint, so you rarely need per-tenant connection config. Where you do need isolation, use channel namespaces (tenant.{slug}.orders.{id}) rather than separate Ably apps — capabilities and token auth in routes/channels.php stay in one place.
Integration tips.
BROADCAST_CONNECTION value per environment so local/testing can stay on log or null and never touch Ably during test runs.routes/channels.php callbacks cheap — the auth endpoint is hit on every subscribe and re-auth, and it runs inside your normal request cycle.broadcastWith() payloads kept small; Ably messages have size ceilings and per-message costs.ABLY_KEY produces auth failures that surface as 401s from Ably rather than clear Laravel exceptions. Verify the appId.key:secret triple is intact (no quotes, no trailing newline) and that the key has publish capability on the channel namespace you're using.php artisan config:cache after changing BROADCAST_CONNECTION or the key — a cached config is the second most common "nothing arrives" cause.ShouldBroadcast pushes the HTTP publish onto the queue; if no worker runs, the event silently never leaves the app. Event::fake() in tests will pass while production shows nothing, so add at least one non-faked integration smoke test.PrivateChannel don't protect themselves — a missing or wrong-shaped Broadcast::channel() callback (or a callback returning a truthy non-boolean) is the actual risk. Test the auth endpoint directly with actingAs for each channel pattern.laravel.log. If the message appears in the Ably console but not in the browser, the problem is client-side subscribe/auth; if it never appears, it's the queue, config, or key. This splits the surface in half in one step.broadcastAs() prefixing. Laravel prefixes the event name with . for custom names; the Echo listener must match exactly. Ably shows the raw name in the console, which is the fastest way to confirm the mismatch.whisper) are restricted. Server-side broadcasting (the normal Laravel pattern) is unaffected, but don't assume arbitrary client→client messaging works out of the box; channel capabilities gate it and Laravel's whisper path needs explicit verification on your plan.ably/ably-php version satisfies the constraint and re-run the broadcast smoke test after composer update. If you're on Laravel 10/11, the release changes nothing for you; if you're moving to Laravel 13, this is the minimum version to pin.How can I help you explore Laravel packages today?