Getting started: Pub/Sub in Laravel

This guide will get you started with Ably Pub/Sub in Laravel 12.

It will take you through the following steps:

  • Create a client and establish a connection to Ably with:
    • The Laravel Broadcaster an independent service provider library for Laravel using ably-php
    • Echo, a Laravel Echo fork that adds native Ably integration for Laravel's realtime broadcasting features.
  • Create an event to broadcast (publish) messages from the backend.
  • Set up a frontend client to receive (subscribe) and publish messages to a channel.

Prerequisites

  1. Sign up for an Ably account.
  2. Create a new app, and create your first API key.
  3. Your API key will need the publish, presence, and history capabilities.
  4. Install the Ably CLI:
npm install -g @ably/cli
  1. Run the following to log in to your Ably account and set the default app and API key:
ably login

ably apps switch
ably auth keys switch
  1. Install PHP version 8.2 or greater and Laravel version 12.0 or greater.
  2. Create a new Laravel project and install the Ably Broadcaster for Laravel:
# Create a new Laravel project
composer create-project laravel/laravel ably-laravel-quickstart
cd ably-laravel-quickstart

# Install the Ably Broadcaster for Laravel
composer require ably/laravel-broadcaster

# Setup API route
php artisan install:api
  1. Now run the two dev servers (PHP and Vite) in separate Terminal windows:
php artisan serve # http://localhost:8000
npm install
npm run dev

Step 1: Configure the Ably Laravel SDK

The Ably Laravel SDK integrates with Laravel's broadcasting system. You need to configure it to establish a connection with Ably.

First, add your Ably credentials to your .env file:

1

2

BROADCAST_CONNECTION=ably
ABLY_KEY=demokey:*****
API key:
DEMO ONLY

In Laravel 12, broadcasting is enabled by default. Verify that BroadcastServiceProvider is included in bootstrap/providers.php:

PHP

1

2

3

4

5

6

<?php

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\BroadcastServiceProvider::class,
];

Publish the broadcasting configuration file if it doesn't exist:

php artisan install:broadcasting --ably

This will create the config/broadcasting.php file and add the Ably configuration automatically.

Step 2: Create the event that will be broadcast

To send events from Laravel to Ably, create an event file by running the following command in your terminal:

php artisan make:event PublicMessageEvent

This command will create a PublicMessageEvent.php file in the app/Events directory. Replace the content of the file with following to handle the broadcasting of messages to a public channel:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

/**
 * Event for broadcasting public messages.
 */
class PublicMessageEvent implements ShouldBroadcastNow
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $channelName;
    public $message;

    /**
     * Create a new event instance.
     */
    public function __construct($channelName, $message)
    {
        $this->channelName = $channelName;
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<\Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [new Channel($this->channelName)];
    }
}

Step 3: Add an API route to broadcast

To handle API requests from frontend, open the routes/api.php file and add following route:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Events\PublicMessageEvent;

Route::post('/public-event', function (Request $request) {
    $channelName = $request->input('channelName');
    $message = $request->input('message');
    broadcast(new PublicMessageEvent($channelName, $message));
    return response()->json(['success' => true, 'message' => 'Event broadcasted']);
})->middleware('throttle:60,1'); // 60 requests/minute are allowed.

This endpoint public-event will accept POST requests with channelName and message parameters, and broadcast the event to the specified channel.

Step 4: Test the API route

To test the API route, first you need at least one client subscribed to the channel. You can use the Ably CLI to subscribe to a channel:

ably channels subscribe public:my-first-channel

Now you can publish a message to the channel using the API route you created in Step 3. You can use curl or any HTTP client to send a POST request:

curl --location --request POST 'localhost:8000/api/public-event' \
--header 'Content-Type: application/json' \
--data-raw '{
    "channelName":"my-first-channel",
    "message":"A message sent from my first client!"
}'

Step 5: Frontend setup

Install Ably's Pub/Sub JavaScript SDK, Ably's fork of laravel-echo, and axios in your Laravel project:

npm install @ably/laravel-echo ably axios

Create or update resources/js/bootstrap.js to include Echo configuration:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import axios from 'axios';
window.axios = axios;

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

// Configure Laravel Echo with Ably
import Echo from '@ably/laravel-echo';
import * as Ably from 'ably';

window.Ably = Ably;

window.Echo = new Echo({
    broadcaster: 'ably',
    key: import.meta.env.VITE_ABLY_KEY, // Add this to your .env file as VITE_ABLY_KEY
});

Step 6: Create a frontend file to listen for events

Create a new CSS stylesheet resources/css/echo.css and add the Tailwind imports:

@tailwind base;
@tailwind components;
@tailwind utilities;

Now create a new Blade view file named echo.blade.php in the resources/views directory, which will be the page rendered in your browser as your front end client to publish and subscribe to a channel:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

<!DOCTYPE html>
<html>
<head>
    <title>Laravel Echo with Ably</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    @vite(['resources/css/app.css','resources/css/echo.css', 'resources/js/app.js'])
</head>
<body class="font-sans max-w-4xl mx-auto p-5 bg-gray-50 leading-relaxed">
    <h1 class="text-slate-800 text-4xl mb-8 text-center font-bold tracking-tight">Laravel Echo with Ably Test</h1>

    <form id="messageForm" class="my-5 p-5 bg-white rounded-lg shadow-sm">
        <h3 class="text-slate-700 text-xl mb-4 font-semibold">Send a Message</h3>
        <input type="text" id="channelName" placeholder="Channel name" value="my-first-channel"
               class="w-48 px-4 py-3 m-1 border-2 border-gray-200 rounded-md text-sm bg-white transition-colors focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
        <input type="text" id="messageInput" placeholder="Enter your message" required
               class="w-48 px-4 py-3 m-1 border-2 border-gray-200 rounded-md text-sm bg-white transition-colors focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-100">
        <button type="submit" class="px-6 py-3 m-1 bg-blue-600 text-white border-none rounded-md text-sm font-semibold cursor-pointer transition-colors hover:bg-blue-700 active:translate-y-px">Send Message</button>
    </form>

    <div id="messages" class="border-2 border-gray-200 rounded-lg p-4 h-80 overflow-y-auto my-5 bg-white shadow-sm">
        <p class="message py-2 border-b border-gray-100 text-gray-600"><strong>Listening for messages...</strong></p>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Echo instance:', window.Echo);

            console.log('Attempting to join channel: my-first-channel');

            const channel = Echo.channel('my-first-channel')
                .subscribed(() => {
                    console.log('Successfully subscribed to channel');
                })
                .listenToAll((eventName, data) => {
                    console.log("Event :: " + eventName + ", data is :: " + JSON.stringify(data));
                })
                .listen('PublicMessageEvent', (data) => {
                    document.getElementById('messages').innerHTML +=
                        `<div class="message py-2 border-b border-gray-100 text-gray-600"><strong>Received:</strong> ${data.message}</div>`;
                })
                .error((err) => {
                    if (err?.statusCode === 401){
                        alert("You don't have the access to join this public room.");
                    } else {
                        alert("An error occurred while trying to join a public room, check the console for details.");
                    }
                    console.error(err);
                });

            // Handle form submission
            document.getElementById('messageForm').addEventListener('submit', function(e) {
                e.preventDefault();

                const channelName = document.getElementById('channelName').value;
                const message = document.getElementById('messageInput').value;
                const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

                if (!message.trim()) {
                    alert('Please enter a message');
                    return;
                }

                // Send message to Laravel API
                fetch('/api/public-event', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-CSRF-TOKEN': csrfToken,
                        'Accept': 'application/json'
                    },
                    body: JSON.stringify({
                        channelName: channelName,
                        message: message
                    })
                })
                .then(response => response.json())
                .then(data => {
                    console.log('Message sent successfully:', data);
                    document.getElementById('messageInput').value = ''; // Clear the input
                    document.getElementById('messages').innerHTML +=
                        `<div class="message py-2 border-b border-gray-100 text-gray-600"><strong>Sent:</strong> ${message} (to ${channelName})</div>`;
                })
                .catch(error => {
                    console.error('Error sending message:', error);
                    alert('Error sending message. Check console for details.');
                });
            });
        });
    </script>
</body>
</html>

Add the route to serve the frontend file in routes/web.php to expose this new file to the browser:

PHP

1

2

3

4

5

6

7

8

9

10

11

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/echo', function () {
    return view('echo');
});

In your browser, open the url http://localhost:8000/echo to see the frontend page.

Use curl or any HTTP client to send a POST request, you should see the message appear in the #messages div on the page:

curl --location --request POST 'localhost:8000/api/public-event' \
--header 'Content-Type: application/json' \
--data-raw '{
    "channelName":"my-first-channel",
    "message":"A message sent from my first client!"
}'
Screenshot showing the Laravel Echo with Ably test page running in a browser

Next steps

Continue to explore the documentation with Laravel as the selected language:

Read more about the concepts covered in this guide:

  • Read more about Ably Laravel Echo
  • Fetch message history in your apps
  • Learn more about Laravel Broadcasting
  • Explore the Ably Laravel Broadcast app - a comprehensive example that builds upon this guide with features like:
    • User authentication (registration + login)
    • Public channels for guests
    • Private channels with presence for authenticated users
    • Typing indicators
    • Multi-room support

You can also explore the Ably CLI further, or visit the Pub/Sub API references.

Select...