10 min readUpdated Apr 8, 2024

Building a realtime chat app with Laravel using WebSockets

Building a realtime chat app with Laravel using WebSockets
AblyAbly

You use realtime communication every day. It is the simultaneous exchange of information between a sender and a receiver with almost zero latency. Internet, landlines, mobile/cell phones, instant messaging (IM), internet relay chat, videoconferencing, teleconferencing, and robotic telepresence are all examples of realtime communication systems.

In this tutorial, you’ll learn how to build a web based realtime chat app using Laravel, Ably and Vue.js. You’ll use Vue.js to build the frontend/UI and Laravel as a backend to interact with Ably Realtime APIs to facilitate realtime communication. By building this type of application, you’ll learn about the core concepts for building realtime apps using the Laravel-Ably integration.

Please note that you can use any frontend framework (react, angular, next.js etc.) to build the Laravel chat app and there is no dependency on any specific framework.

The source code for the project is available in this GitHub repo.

Architecture of the realtime chat app

Before getting started, you should familiarize yourself with the tech stack that you’ll be using in this tutorial to build your chat application.

Laravel

Laravel is a PHP-based web application framework with expressive, elegant syntax. The web development community loves Laravel because it provides an amazing developer experience and supports advanced concepts, like queues, dependency injection, unit testing, and integration testing. Laravel is a great choice for building realtime apps, and it already comes with support for Ably to "broadcast" your server-side Laravel events over a WebSocket connection.

Ably

Ably is a pub/sub messaging platform that powers synchronized digital experiences in realtime for millions of simultaneously connected devices around the world. Ably offers WebSockets, stream resume, history, presence, and managed third-party integrations to make it simple to build, extend, and deliver digital realtime experiences at scale. Ably makes use of different transports to make sure data is delivered in realtime.

Vue.js

Vue is a frontend javaScript framework for building user interfaces. It builds on top of standard HTML, CSS, and JavaScript and provides a declarative and component-based programming model that helps you efficiently develop user interfaces, be they simple or complex.

Laravel broadcasting

Laravel broadcasting is a feature provided by Laravel as a way for a client (frontend) to publish and receive information in realtime via channels. A channel is a logical communication medium that helps to publish and subscribe to information over a WebSocket connection. There are 3 types of channels.

1. Public channels

  • The client doesn't need to be authenticated (logged in) to use public channels.
  • The client can only subscribe to the data on this channel. To publish data, it needs to be done via the laravel-server (backend) i.e. client1 => laravel => ably => client 2, client 3, client 4...

2. Private channels

  • The client needs to be authenticated to use private channels.
  • The client is directly able to publish and subscribe to data through Ably using client-events. i.e. client1 => ably => client 2, client 3, client 4...
  • The client can also publish data through laravel-backend, as done for public channels.

3. Presence channels

  • These are similar to private channels, offering all of the features of private channels, as well as a way to register client presence on the channel.

In this blog we will only look at public channels. So, the architecture of the realtime chat app will look like this:

The architecture of the chat app using Laravel and WebSockets

The chat app uses laravel + ably-broadcaster and Ably at server side (backend) and vue.js + ably-laravel-echo at client side (frontend).

Laravel-server is used for authenticating/authorizing clients and broadcasting information to ably on behalf of clients over a given channel. In the current example, since public channels don’t need any sort of authentication, it will only be used to broadcast information.

Clients (guest-users) use HTTP connection to post a message with a channel-name to Laravel-server. Laravel-server publishes the same message to Ably (on the same channel) using a named event. Ably is responsible for sending this information to clients (subscribed to the same channel) using the provided event over a WebSocket connection. Guest users consume this event on the client-side using the Laravel Echo library.

Publishing a message on a channel is done using an event. Events are the primary method of packaging a message on the channel.

In simpler terms, when a message is published on a channel with an eventName, it will be received by a subscriber matching the same channelName and eventName.

Setting up the environment

Here is what we will need to get started.

Prerequisites

Setting up the basic project structure

Execute the following command in the terminal to create a Laravel project.

composer create-project laravel/laravel ably-laravel-chat-app

Run the following commands to get the auto-generated app running.

cd ably-laravel-chat-app
composer install
npm install
php artisan key:generate
php artisan serve // run in a separate terminal
npm run watch // run in a separate terminal 

Finally, open localhost:8000 in your browser, and you’ll see your Laravel project running.

The Laravel homepage

Setting up your Ably account

To start, you need to create an Ably account if you don’t have one. Next, you need to grab the API key from the Ably dashboard:

  1. Visit your app dashboard and click Create New App with name Chat-App.
  2. Once the app has been created, go to the API Keys tab. Copy the first key with the name Root. Keep the key safe. The key shouldn't be exposed to the client code.

That’s it. Your Ably account is ready to support realtime messaging.

Setting up Ably broadcaster on Laravel

Start by installing the official Ably Broadcaster for Laravel using composer:

composer require ably/laravel-broadcaster

Next, follow the Setup section to configure the Ably broadcaster.

Create PublicMessageEvent Class

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 files with following:

<?php

namespace App\Events;

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

class PublicMessageEvent implements ShouldBroadcast
{
    public $channelName;
    public $message;

    use Dispatchable, InteractsWithSockets, SerializesModels;

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

    /**
     * Get the channels the event should broadcast on.
     * https://laravel.com/docs/broadcasting#model-broadcasting-channel-conventions
     * 
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return [new Channel($this->channelName)];
    }
}

The code above specifies:

  1. The PublicMessageEvent class that implements the ShouldBroadcast interface.
  2. By default the class name is used as an event name. This means eventName will be PublicMessageEvent. You can change eventName using broadcastAs.
  3. The broadcastOn method that is responsible for returning the channel/list of channels on which the event should broadcasted.

Broadcasting using PublicMessageEvent class

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

Route::post('/public-event', function (Request $request) {
    $channelName = $request->post('channelName');
    $message = $request->post('message');
    broadcast(new PublicMessageEvent( $channelName, $message ));
})->middleware('throttle:60,1'); // 60 requests/minute are allowed.

The code above specifies:

  1. A new API route to handle POST request from client. This API route will be available at localhost:8000/api/public-event.
  2. When called, the message is broadcasted to ably on the given channel with eventName as PublicMessageEvent.
  3. Throttle to 60 requests per minute for each IP address. It will return an error for requests exceeding the given limit. This is mainly to avoid spamming on public channels.

To check if the code is working correctly so far:

  1. Restart the server using php artisan serve.
  2. Attach to channel public:community for new messages using the Ably Dev Console.
  3. Try sending a POST request to the localhost:8000/api/public-event endpoint using curl, Postman or any other tool with channelName and message as POST method body.
curl --location --request POST 'localhost:8000/api/public-event' \
--header 'Content-Type: application/json' \
--data-raw '{
    "channelName":"public:community",
    "message":"Hi there"
}'
  1. Check for this message in the Ably Dev Console under the attached channel tab.

Next, we will look at setting up frontend to send and receive messages.

Setting up Laravel Frontend

Following packages need to be installed for proper communication with Laravel and Ably.

  1. Laravel Echo: This JavaScript library allows clients/users to subscribe to channels and listen to the events sent from ably.
  2. Axios: This package helps to make HTTP requests from client to laravel-server.
npm install @ably/laravel-echo ably@1.x axios

Initialize Laravel-Echo

In the resources/js/bootstrap.js file, add the following snippet as per the Echo Setup Section.

import Echo from '@ably/laravel-echo';

window.Ably = require('ably');

// Create new echo client instance using ably-js client driver.
window.Echo = new Echo({
    broadcaster: 'ably',
});

// Register a callback for listing to connection state change 
window.Echo.connector.ably.connection.on((stateChange) => {
    console.log("LOGGER:: Connection event :: ", stateChange);
    if (stateChange.current === 'disconnected' && stateChange.reason?.code === 40142) { // key/token status expired
        console.log("LOGGER:: Connection token expired https://help.ably.io/error/40142");
    }
});

Building the frontend UI

Please take a look at app.js, ChatComponent.vue and MessageComponent.vue from the existing repository under resources/js/ for building the UI for your chat app.

Sending messages

In the ChatComponent.vue file, you can see the broadcastMessageOnPublicChannel method is used for broadcasting data on a given public channel using axios.

broadcastMessageOnPublicChannel() {
    const message = this.message?.trim();
    if(!message)
        return;
    const publicChannelName = this.getActiveChannel().name;
    const broadcastUrl = window.location.origin + "/api/public-event";
    axios.post(broadcastUrl, { channelName : publicChannelName, message });
    this.message = null;
}

The code above specifies:

  1. publicChannelName is generally prefixed with public:.
  2. When broadcastMessageOnPublicChannel is called, it sends a message from the client to laravel-server at endpoint /api/public-event using axios.
  3. Axios sends a HTTP POST request with channelName and message as a request body.

Receiving messages

In the ChatComponent.vue file, the listen method describes handler for PublicMessageEvent.

Echo.channel(channelName)
.subscribed(() => // Do stuff when channel is attached )
.listenToAll(() => // Do stuff when any event is received )
.listen('PublicMessageEvent', (data) => {
    const channel = this.getChannelByName(channelName, 'public');
    this.updateBroadcastNotificationOnUI(channel, data)
})

The code above specifies:

  1. Public channel is created using Echo.Channel(channelName).
  2. Actual publicChannelName is accessed using Echo.Channel(channelName).name and is prefixed with public:. i.e. If channelName is provided as community, internally it uses channel name as public:community while communicating with ably.
  3. The method listenToAll registers a handler which gets called for any event received.
  4. The method listen registers a handler for a specific PublicMessageEvent and updates the received message on the UI once called.

Testing the chat app

To test your chat app, open the localhost:3000 in at least two browser tabs. Join the public room in each tab and broadcast messages to each other. For more information, refer to Guest Mode under the Usage Section.

Testing your chat app using a public room

Conclusion

That’s it! In this tutorial, you learned how to create a realtime chat app using Laravel and Ably for guest users. In a future blog, we will cover how to add chat functionality for authenticated (logged in) users. Depending on your requirements, you can also discover how to add more features to your chat app in the Ably chat reference guide.

We would love to hear about how you plan to use Ably in your applications. Join our Discord server and post your ideas for builds in the showcase channel to share, we’re currently giving out Ably tees to members who participate!

Further reading

Join the Ably newsletter today

1000s of industry pioneers trust Ably for monthly insights on the realtime data economy.
Enter your email