PHPackages                             7amoood/laravel-appsync-broadcaster - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. 7amoood/laravel-appsync-broadcaster

ActiveLibrary[API Development](/categories/api)

7amoood/laravel-appsync-broadcaster
===================================

AWS AppSync broadcaster for Laravel Broadcasting - Real-time communication using AWS AppSync Events API

v1.0.0(1w ago)153MITPHPPHP ^8.1

Since Mar 13Pushed 1w agoCompare

[ Source](https://github.com/7amoood/laravel-appsync-broadcaster)[ Packagist](https://packagist.org/packages/7amoood/laravel-appsync-broadcaster)[ RSS](/packages/7amoood-laravel-appsync-broadcaster/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (12)Versions (2)Used By (0)

Laravel AppSync Broadcaster
===========================

[](#laravel-appsync-broadcaster)

A Laravel Broadcasting driver for AWS AppSync that enables real-time communication using AWS AppSync GraphQL subscriptions.

Features
--------

[](#features)

- **Real-time Broadcasting**: Leverage AWS AppSync for real-time messaging
- **Channel Support**: Support for public, private, and presence channels
- **Authentication**: Cognito-based authentication for private channels
- **Frontend Integration**: WebSocket connection examples for JavaScript/Node.js
- **Retry Logic**: Built-in retry mechanism with exponential backoff
- **Error Handling**: Comprehensive error handling and logging
- **Laravel Integration**: Seamless integration with Laravel's Broadcasting system

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require 7amoood/laravel-appsync-broadcaster
```

The service provider will be automatically registered due to Laravel's package auto-discovery.

Configuration
-------------

[](#configuration)

1. Publish the configuration file:

```
php artisan vendor:publish --tag=appsync-config
```

2. Add the following environment variables to your `.env` file:

```
APPSYNC_NAMESPACE=your-app-namespace-or-remove-it-to-set-default
APPSYNC_APP_ID=your-appsync-app-id
APPSYNC_EVENT_REGION=us-east-1
APPSYNC_COGNITO_POOL=your-cognito-pool-id
APPSYNC_COGNITO_REGION=us-east-1-or-remove-it-for-same
APPSYNC_COGNITO_CLIENT_ID=your-cognito-client-id
APPSYNC_COGNITO_CLIENT_SECRET=your-cognito-client-secret
```

3. Set AppSync as your default broadcast driver in `.env`:

```
BROADCAST_DRIVER=appsync
```

> **Note**: The AppSync broadcasting connection is automatically registered. You don't need to manually add it to your `config/broadcasting.php` file.

Usage
-----

[](#usage)

### Basic Broadcasting

[](#basic-broadcasting)

Use Laravel's standard broadcasting features:

```
// In your event class
class OrderUpdated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('orders/' . $this->order->id);
    }
}

// Dispatch the event
event(new OrderUpdated($order));
```

### Presence Channels

[](#presence-channels)

For presence channels, use the `presence-` prefix:

```
public function broadcastOn()
{
    return new PresenceChannel('chat/room/' . $this->roomId);
}
```

### Manual Broadcasting

[](#manual-broadcasting)

You can also broadcast manually:

```
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('test-channel')->send('test-event', [
    'message' => 'Hello World!'
]);
```

Channel Authentication
----------------------

[](#channel-authentication)

For private and presence channels, you need to define authorization logic in your `routes/channels.php`:

```
// Private channel authorization
Broadcast::channel('orders/{orderID}', function ($user, $orderID) {
    return (int) $user->id === (int) Order::find($orderID)->user_id;
});

// Presence channel authorization
Broadcast::channel('chat/room/{roomId}', function ($user, $roomId) {
    if ($user->canAccessRoom($roomId)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});
```

Frontend Integration
--------------------

[](#frontend-integration)

To subscribe to channels from your frontend application, you need to establish a WebSocket connection to AWS AppSync. Here's how to do it:

### JavaScript/Node.js Example

[](#javascriptnodejs-example)

```
const WebSocket = require("ws");
const axios = require("axios");

// Your AppSync configuration
const API_ID = "your-appsync-app-id";
const REGION = "your-region"; // e.g., "us-east-1"
const TOKEN = 'your-user-token'; // JWT token for authenticated users
const CHANNEL = "default/private-orders/123"; // Channel to subscribe to

// AppSync endpoints
const AUTH_URL = 'https://your-app.com/broadcasting/auth';
const REALTIME_DOMAIN = `${API_ID}.appsync-realtime-api.${REGION}.amazonaws.com`;
const HTTP_DOMAIN = `${API_ID}.appsync-api.${REGION}.amazonaws.com`;

async function connectAppSync() {
    try {
        // Step 1: Authenticate with your Laravel app to get AppSync API key
        const res = await axios.post(AUTH_URL, {
            channel_name: CHANNEL,
        }, {
            headers: { Authorization: `Bearer ${TOKEN}` }
        });
        const API_KEY = res.data.auth;
        console.log("API_KEY obtained:", API_KEY);

        // Step 2: Connect to AppSync WebSocket
        const headerObj = {
            host: HTTP_DOMAIN,
            Authorization: API_KEY
        };

        // Base64 encode headers (URL safe)
        const header = Buffer.from(JSON.stringify(headerObj))
            .toString("base64")
            .replace(/\+/g, "-")
            .replace(/\//g, "_")
            .replace(/=+$/, "");

        const url = `wss://${REALTIME_DOMAIN}/event/realtime`;
        const ws = new WebSocket(url, [`header-${header}`, "aws-appsync-event-ws"]);

        // Step 3: Handle connection
        ws.on("open", () => {
            console.log("✅ Connected to AppSync Realtime");
            ws.send(JSON.stringify({ type: "connection_init" }));
        });

        // Step 4: Handle messages
        ws.on("message", (msg) => {
            const data = JSON.parse(msg.toString());
            console.log("📩 Message:", data);

            // Handle connection acknowledgement
            if (data.type === "connection_ack") {
                console.log("✅ Connection ACK received, subscribing to channel");

                // Subscribe to the channel
                ws.send(JSON.stringify({
                    id: crypto.randomUUID(), // Generate unique subscription ID
                    type: "subscribe",
                    channel: CHANNEL,
                    authorization: {
                        "Authorization": API_KEY,
                        "host": HTTP_DOMAIN
                    }
                }));
            }

            // Handle incoming data
            if (data.type === "data") {
                const event = JSON.parse(data.event);
                console.log("📦 Event received:", event);

                // Handle your event data
                switch (event.event) {
                    case 'OrderUpdated':
                        console.log('Order updated:', event.data);
                        break;
                    case 'UserMessage':
                        console.log('New message:', event.data);
                        break;
                    // Add more event handlers as needed
                }
            }
        });

        // Handle disconnection
        ws.on("close", () => console.log("❌ Disconnected"));
        ws.on("error", (err) => console.error("⚠️ Error:", err));

    } catch (err) {
        console.error("Error connecting to AppSync:", err.message);
    }
}

// Start the connection
connectAppSync();
```

### Browser Example (ES6+)

[](#browser-example-es6)

```
class AppSyncSubscriber {
    constructor(config) {
        this.apiId = config.apiId;
        this.region = config.region;
        this.authUrl = config.authUrl;
        this.token = config.token;
        this.ws = null;
        this.subscriptions = new Map();
    }

    async connect() {
        const realtimeDomain = `${this.apiId}.appsync-realtime-api.${this.region}.amazonaws.com`;
        const httpDomain = `${this.apiId}.appsync-api.${this.region}.amazonaws.com`;

        try {
            // Get API key from Laravel
            const response = await fetch(this.authUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${this.token}`
                },
                body: JSON.stringify({ channel_name: 'default' })
            });

            const { auth: apiKey } = await response.json();

            // Create WebSocket connection
            const headerObj = { host: httpDomain, Authorization: apiKey };
            const header = btoa(JSON.stringify(headerObj))
                .replace(/\+/g, "-")
                .replace(/\//g, "_")
                .replace(/=+$/, "");

            this.ws = new WebSocket(
                `wss://${realtimeDomain}/event/realtime`,
                [`header-${header}`, "aws-appsync-event-ws"]
            );

            this.ws.onopen = () => {
                console.log("Connected to AppSync");
                this.ws.send(JSON.stringify({ type: "connection_init" }));
            };

            this.ws.onmessage = (event) => {
                const data = JSON.parse(event.data);
                this.handleMessage(data, apiKey, httpDomain);
            };

            this.ws.onclose = () => console.log("Disconnected");
            this.ws.onerror = (err) => console.error("WebSocket error:", err);

        } catch (error) {
            console.error("Connection failed:", error);
        }
    }

    handleMessage(data, apiKey, httpDomain) {
        if (data.type === "connection_ack") {
            this.onReady && this.onReady();
        } else if (data.type === "data") {
            const event = JSON.parse(data.event);
            this.onEvent && this.onEvent(event);
        }
    }

    subscribe(channel, callback) {
        const id = crypto.randomUUID();
        this.subscriptions.set(id, callback);

        this.ws.send(JSON.stringify({
            id,
            type: "subscribe",
            channel,
            authorization: {
                "Authorization": this.apiKey,
                "host": this.httpDomain
            }
        }));

        return id; // Return subscription ID for unsubscribing
    }

    unsubscribe(subscriptionId) {
        this.ws.send(JSON.stringify({
            id: subscriptionId,
            type: "unsubscribe"
        }));
        this.subscriptions.delete(subscriptionId);
    }
}

// Usage
const subscriber = new AppSyncSubscriber({
    apiId: 'your-appsync-app-id',
    region: 'us-east-1',
    authUrl: '/broadcasting/auth',
    token: 'your-jwt-token'
});

subscriber.onReady = () => {
    console.log("Ready to subscribe!");

    // Subscribe to channels
    subscriber.subscribe('default/private-orders/123', (event) => {
        console.log('Order event:', event);
    });
};

subscriber.onEvent = (event) => {
    console.log('Received event:', event);
};

subscriber.connect();
```

### Channel Name Format

[](#channel-name-format)

Channels follow this format: `{namespace}/{channel_type}-{channel_name}`

- **namespace**: From your `APPSYNC_NAMESPACE` config (default: "default")
- **channel\_type**:
    - `public` for public channels
    - `private` for private channels
    - `presence` for presence channels
- **channel\_name**: Your channel identifier

Examples:

- `default/orders` (public channel)
- `default/private-user/123` (private channel)
- `default/presence-chat/room1` (presence channel)

### Authentication Flow

[](#authentication-flow)

1. **Frontend** sends channel name to your Laravel app's `/broadcasting/auth` endpoint
2. **Laravel** validates the request and returns an AppSync API key
3. **Frontend** uses the API key to establish WebSocket connection
4. **Frontend** subscribes to specific channels using the authenticated connection

### Event Structure

[](#event-structure)

Events received from AppSync have this structure:

```
{
  event: "EventName",           // The broadcast event name
  channel: "default/orders",    // Channel name
  data: {                       // Your event data
    order_id: 123,
    status: "completed"
  }
}
```

AWS AppSync Setup
-----------------

[](#aws-appsync-setup)

1. Create an AppSync Event API in your AWS Console
2. Set up Cognito User Pool for authentication
3. Create a Cognito App Client with client credentials flow enabled
4. Attach Cognito to AppSync as auth method

Error Handling
--------------

[](#error-handling)

The broadcaster includes comprehensive error handling:

- **Connection failures**: Automatic retry with exponential backoff
- **Authentication errors**: Detailed logging and error messages
- **Partial failures**: Continues broadcasting to other channels if some fail
- **Configuration validation**: Validates required configuration parameters

Logging
-------

[](#logging)

All errors and important events are logged using Laravel's logging system. Check your logs for:

- Authentication failures
- Broadcast failures
- Retry attempts
- Configuration errors

Requirements
------------

[](#requirements)

- PHP ^8.0
- Laravel ^9.0|^10.0|^11.0
- AWS AppSync API
- AWS Cognito User Pool

License
-------

[](#license)

This package is open-sourced software licensed under the [MIT license](LICENSE).

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security-related issues, please email the package maintainer instead of using the issue tracker.

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance98

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

8d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5ed136e21d6ee0b6d092497fd1558888a0712d9acb137dc027ecf63b0e17735c?d=identicon)[7amoood](/maintainers/7amoood)

---

Top Contributors

[![7amoood](https://avatars.githubusercontent.com/u/41032112?v=4)](https://github.com/7amoood "7amoood (1 commits)")

---

Tags

laravelawsWebSocketspusherrealtimesubscriptionsBroadcastingablyappsyncevent apireplacement of pusher

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/7amoood-laravel-appsync-broadcaster/health.svg)

```
[![Health](https://phpackages.com/badges/7amoood-laravel-appsync-broadcaster/health.svg)](https://phpackages.com/packages/7amoood-laravel-appsync-broadcaster)
```

###  Alternatives

[joggapp/laravel-aws-sns

Laravel package for the SNS events by AWS

3169.3k](/packages/joggapp-laravel-aws-sns)[casperlaitw/laravel-fb-messenger

A Laravel Package to Integrate Facebook Messenger API

10431.5k](/packages/casperlaitw-laravel-fb-messenger)[coreproc/nova-echo

Adds Laravel Echo with your broadcast configuration to your Laravel Nova app

1996.2k2](/packages/coreproc-nova-echo)[vahidkaargar/bamboo-card-portal

This is Bamboocardportal.com package

161.9k](/packages/vahidkaargar-bamboo-card-portal)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
