nc/faye-client
A Laravel client for the Faye real-time messaging server, making it easy to publish events and subscribe to channels from PHP applications. Integrates Faye/Bayeux pub-sub into Laravel apps for live updates and notifications.
nc/faye-client is a thin server-side publisher for a Faye Bayeux server. It does not subscribe, long-poll, or manage websockets from PHP — those belong in the browser JS client. PHP’s job is to POST a message to Faye’s /faye endpoint so the Node processes can fan it out.
1. Install
composer require nc/faye-client
The package is from 2014 and pulls an old HTTP adapter (Buzz in most releases). If Composer refuses on a modern Laravel, check the adapter constraint before assuming the package itself is broken.
2. Run a Faye server. Nothing in this package starts Faye; you need a Node process:
// faye-server.js
const faye = require('faye');
const server = new faye.NodeAdapter({ mount: '/faye', timeout: 45 });
server.listen(8000);
3. First publish. In php artisan tinker (or a route you’ll delete afterwards), confirm connectivity end to end:
use Nc\FayeClient\Client;
use Nc\FayeClient\Adapter\BuzzAdapter;
use Buzz\Browser;
$faye = new Client(new BuzzAdapter(new Browser()), 'http://localhost:8000/faye');
$faye->publish('/demo', ['message' => 'hello from Laravel']);
Then subscribe in the browser console on any page that loads faye-browser.js:
const client = new Faye.Client('http://localhost:8000/faye');
client.subscribe('/demo', msg => console.log(msg));
Where to look first
vendor/nc/faye-client/README.md and src/Nc/FayeClient/ — confirm the exact constructor signature and which adapters ship with your installed version (they changed between tags).Adapter/BuzzAdapter, etc.) — that is where the HTTP call and error handling actually live.First real use case: publish from a Laravel event listener after a model is created, so a browser dashboard updates without polling.
Bind the client once. Don’t construct it per call; wrap it in a singleton and keep the endpoint in config/services.php so config:cache works.
// app/Providers/FayeServiceProvider.php
public function register(): void
{
$this->app->singleton(Client::class, function () {
return new Client(
new BuzzAdapter(new Browser()),
config('services.faye.endpoint') // e.g. http://localhost:8000/faye
);
});
}
Wrap it in your own service. The package gives you publish($channel, $data); your app should speak in domain terms, not Bayeux terms.
class FayePublisher
{
public function __construct(private Client $faye) {}
public function toUser(int $userId, string $event, array $payload = []): void
{
$this->faye->publish("/users/{$userId}", [
'event' => $event,
'payload' => $payload,
]);
}
}
Publish from event listeners, not controllers. Laravel events already give you a natural seam, and listeners are trivially queued.
class PublishOrderShipped
{
public function handle(OrderShipped $event): void
{
app(FayePublisher::class)->toUser(
$event->order->user_id,
'order.shipped',
['order_id' => $event->order->id]
);
}
}
Queue the publish. A dead or slow Faye server should never block a web request. Implement ShouldQueue on the listener (or call the publisher from a job) and set retry/backoff; a publish failure is usually safe to retry.
Use a custom broadcaster on Laravel 5.1+. If you already use broadcast() / Broadcast::event(), don’t bypass it — register a Faye driver so your controllers and events stay broadcaster-agnostic:
class FayeBroadcaster extends Broadcaster
{
public function __construct(private Client $faye) {}
public function broadcast(array $channels, $event, array $payload = []): void
{
foreach ($channels as $channel) {
$this->faye->publish('/'.$channel, [
'event' => $event,
'payload' => $payload,
]);
}
}
public function auth($request) {}
public function validAuthenticationResponse($request, $result) {}
}
Register it in BroadcastServiceProvider via Broadcast::extend('faye', ...), and drive it with BROADCAST_DRIVER=faye.
Channel conventions matter. Use hierarchical paths (/users/42, /orders/42/status) so the JS side can wildcard-subscribe (/orders/*) and you keep access rules understandable. Prefix everything with / — Faye silently ignores or errors on channels that don’t start with one, depending on version.
Test by faking the adapter, not the network. Because the client depends on an adapter interface, you can bind a spy in tests and assert your listener published to the right channel with the right payload. This is the main reason to keep the client behind your own FayePublisher rather than calling Client directly in listeners.
buzz) is ancient and will likely collide with modern Laravel/Guzzle constraints. Expect to either pin an old dependency tree, fork the client, or replace it with a three-line Http::asForm()->post(...) call. Faye’s publish endpoint is simple enough that the package mostly saves you a JSON shape./faye, the URL must be http://host:port/faye. A trailing slash or / mismatch produces 404s that look like auth failures.clientId management. If you need authenticated or stateful Bayeux sessions, this package won’t help; keep auth/extension logic on the Faye server and out of PHP.[{"error":"..."}]). Log the response body during rollout; do not assume publish() throwing means success and returning means delivery.auth is not involved here.config/services.php under a faye key and reference it with config(). Never read env('FAYE_ENDPOINT') inside the service provider — it returns null after config:cache.DateTime needs formatting; resources will throw).curl the /faye endpoint directly, (2) check the Faye Node console for the incoming channel, (3) check the browser is subscribed to the exact same path including leading /, (4) check for a reverse proxy stripping POST bodies.Client (or your FayePublisher) rather than patching the vendor code.Broadcast driver built on Illuminate\Support\Facades\Http or Redis pub/sub is more maintainable than this package. Keep FayePublisher as the abstraction so the eventual swap is one class, not a search-and-replace.How can I help you explore Laravel packages today?