Laravel API Broadcast Driver: Custom Laravel Driver for Sending Event to API endpoint.
Laravel’s event broadcasting is a powerful feature that provides a seamless method for “broadcasting” your server-side Laravel events via a WebSocket connection. This allows you to share the same event names and data between your client-side JavaScript and server-side Laravel applications, enhancing the efficiency and effectiveness of your development process.
By default, Laravel includes three server-side broadcasting drivers: Laravel Reverb, Pusher Channels, and Ably. These drivers broadcast events server-side via WebSockets to the client side. However, what if you want to broadcast events fluently from server to server? None of the broadcasting drivers currently support this.
This tutorial will create a custom broadcasting driver for broadcasting events over an API endpoint.
To create your custom broadcasting driver, you must extend the Laravel broadcasting system and define the necessary methods to integrate with your custom service.
Here’s a step-by-step guide on how to create a custom broadcasting driver in Laravel:
Step 1: Create the Custom Broadcasting Driver Class
First, create a new class serving as your custom broadcasting driver. This class should implement the Illuminate\Contracts\Broadcasting\Broadcaster
interface and extend the Illuminate\Broadcasting\Broadcasters\Broadcaster
class.
<?php
namespace App\Broadcasting;
use Exception;
use Illuminate\Broadcasting\Broadcasters\Broadcaster;
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
class HttpBroadcaster extends Broadcaster implements BroadcasterContract
{
/**
* The base URL of the broadcasting service.
*
* @var string
*/
private string $baseUrl;
/**
* The token used to authenticate with the broadcasting service.
*
* @var string
*/
private string $token;
/**
* The key used to authenticate with the broadcasting service.
*
* @var string
*/
private string $key;
/**
* The type of authentication used to authenticate with the broadcasting service.
*
* @var string
*/
private string $authType;
/**
* The endpoint to which the broadcast will be sent.
*
* @var string
*/
private string $endpoint;
/**
* The headers to be sent with the broadcast.
*
* @var array
*/
private array $headers = [];
/**
* Whether to verify the SSL certificate of the broadcasting service.
*
* @var bool
*/
private bool $verify = true;
public function __construct(
private array $config = []
) {
// set defaults
$this->setBaseUrl($this->config['base_url'] ?? '');
$this->setToken($this->config['token'] ?? '');
$this->setKey($this->config['key'] ?? '');
$this->setAuthType($this->config['auth'] ?? 'bearer');
$this->setVerify($this->config['verify'] ?? true);
}
public function setBaseUrl(string $url): self
{
$this->baseUrl = rtrim($url, '/');
return $this;
}
public function setToken(string $token): self
{
$this->token = $token;
return $this;
}
public function setKey(string $key): self
{
$this->key = $key;
return $this;
}
public function setAuthType(string $authType): self
{
$this->authType = $authType;
return $this;
}
public function setEndpoint(string $endpoint): self
{
$this->endpoint = ltrim($endpoint, '/');
return $this;
}
public function setHeaders(array $headers): self
{
$this->headers = $headers;
return $this;
}
public function setVerify(bool $verify): self
{
$this->verify = $verify;
return $this;
}
public function client(): PendingRequest
{
if ($this->authType === 'null') {
return Http::withoutVerifying();
}
if ($this->authType === 'basic') {
return Http::withBasicAuth($this->key, $this->token);
}
if ($this->authType === 'digest') {
return Http::withDigestAuth($this->key, $this->token);
}
if ($this->authType === 'key') {
return Http::withHeaders([
'X-Api-Key' => $this->key,
]);
}
return Http::withToken($this->token);
}
/**
* @throws Exception
*/
public function auth($request)
{
}
/**
* @throws Exception
*/
public function validAuthenticationResponse($request, $result)
{
}
/**
* @throws Exception
*/
public function broadcast(array $channels, $event, array $payload = []): void
{
// Convert channels to the format required by your custom broadcaster
$formattedChannels = $this->formatChannels($channels);
// Send the payload to your custom broadcasting service
$response = $this->send($formattedChannels, $event, $payload);
if ($response->failed()) {
throw new Exception($response->reason());
}
}
protected function formatChannels(array $channels): array
{
return array_map(function ($channel) {
return str_replace('private-', '', $channel);
}, $channels);
}
protected function send(array $channels, string $event, array $payload): Response
{
$url = empty($this->endpoint)
? $this->baseUrl . '/' . $event
: $this->baseUrl . '/' . $this->endpoint;
$data = [
'channels' => $channels,
'event' => $event,
'payload' => $payload,
];
$request = $this->client();
if ($this->headers) {
$request->withHeaders($this->headers);
}
if (! $this->verify) {
$request->withoutVerifying();
}
return $request->post($url, $data);
}
}
Step 2: Register the Custom Broadcasting Driver
Next, you need to register your custom broadcasting driver with Laravel. You can do this in a service provider, typically in AppServiceProvider
or a custom provider.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
use App\Broadcasting\HttpBroadcaster;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::extend('http', function ($app, array $config) {
return new HttpBroadcaster($config);
});
}
}
Step 3: Update the Broadcasting Configuration
Now, update the config/broadcasting.php
configuration file to include your custom driver. Add a new entry for your custom broadcaster under the connections
array.
<?php
return [
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY', 'app-key'),
'secret' => env('PUSHER_APP_SECRET', 'app-secret'),
'app_id' => env('PUSHER_APP_ID', 'app-id'),
'options' => [
'host' => env('PUSHER_HOST', '127.0.0.1'),
'port' => env('PUSHER_PORT', 6001),
'scheme' => env('PUSHER_SCHEME', 'http'),
'encrypted' => env('PUSHER_ENCRYPTED', false),
'useTLS' => env('PUSHER_TLS', false),
'cluster' => env('PUSHER_CLUSTER', 'mt1'),
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
'http' => [
'driver' => 'http',
'base_url' => env('HTTP_BROADCASTER_API_ENDPOINT', 'localhost'),
'key' => env('HTTP_BROADCASTER_API_KEY', null),
'token' => env('HTTP_BROADCASTER_API_TOKEN', null),
'verify' => env('HTTP_BROADCASTER_API_VERIFY', true),
// Auth type can be null, 'basic', 'bearer', 'digest', 'key', 'jwt'
'auth' => env('HTTP_BROADCASTER_API_AUTH_TYPE', null),
],
],
];
Step 4: Configure Environment Variables
Add the necessary environment variables for your custom broadcaster in the .env
file.
HTTP_BROADCASTER_API_ENDPOINT=https://example.com/api/broadcasting
HTTP_BROADCASTER_API_TOKEN=long-token
HTTP_BROADCASTER_API_AUTH_TYPE=bearer
HTTP_BROADCASTER_API_VERIFY=true
Step 5: Create an event
When wanting to trigger the following event upon creating a new user, ensure that a Laravel queue system is set up, as this event is queued due to the slow nature of HTTP/HTTPS requests. Learn more about Laravel Queues
<?php
namespace App\Events\User;
use Illuminate\Broadcasting\InteractsWithBroadcasting;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Created implements ShouldBroadcast
{
use Dispatchable;
use SerializesModels;
use InteractsWithBroadcasting;
public function __construct(
public array $payload
){}
public function broadcastQueue(): string
{
return 'default';
}
public function broadcastAs(): string
{
return 'user.created';
}
public function channelName(): string
{
return 'admins';
}
public function broadcastOn(): array
{
return [
new PrivateChannel($this->channelName()),
];
}
public function broadcastConnections(): array
{
return ['http'];
}
}
Step 6: Broadcast event
The event above can be fired like so:
$payload = [
'id' => 1
'username' => 'emeka',
'email' => 'fake@test.com',
];
Created::dispatch($payload);
Step 7: In the second server or microservice
<?php //in api.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BroadcastingController;
Route::controller(BroadcastingController::class)->group(function(){
Route::post('broadcasting', 'broadcasting');
});
<?php // controller
namespace App\Http\Controllers;
class BroadcastingController extends Controller
{
public function broadcasting(string $event)
{
// handle the event
info($request->all(), ['event' => $event]);
}
}