• Blog:

  • Home
  • Company
  • Engineering
  • Developers
  • Edge Messaging
  • Building a realtime chat app with React, Laravel, and WebSockets

    By: Ravgeet Dhillon 13 min read

    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.

    The WebSocket Handbook

    In this tutorial, you’ll learn how to build a realtime public chat app using React.js, Laravel, and Ably. You’ll use React.js to build the frontend/UI and Laravel to interact with Ably Realtime APIs to facilitate realtime communication. Anyone on the internet would be able to use this app to post messages to a public chat room and talk anonymously with other connected users. By building this kind of application, you’ll learn about the relevant concepts for building applications that need real-time data transfer.

    The entire 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.

    React.js

    React.js is an open-source JavaScript library that is used for building reactive user interfaces and is often used for building client side rendered single-page applications. It is very easy to integrate it with TypeScript. Based on this tutorial, you can also perform the same set of actions in Next.js, which is a meta framework for developing universal React.js applications that are both client and server side rendered.

    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.

    The architecture of the realtime chat app will look this:

    Chat app workflow architecture

    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.

    2. Copy the private API key once the app has been created. Keep the key safe; you will use it to authenticate with the Ably service in Laravel and React.js.

    Private key from the Ably dashboard
    Private key from the Ably dashboard


    3. Enable the Pusher protocol support in your Ably app settings to use the Pusher protocol with Ably. You can find this feature in the Protocol Adapter Settings under the Settings tab. In case you’re asked about creating default namespaces, you should click No thanks.

    Updating Ably settings
    Updating Ably settings


    That’s it. Your Ably account is ready to support real-time messaging.

    Setting up the environment

    Here is what you’ll need to get started.

    Prerequisites

    - Node.js: This tutorial uses Node v16.14.0

    - PHP: This tutorial uses PHP v7.4.3

    - Composer: This tutorial uses Composer v2.1.0

    Setting Up the Project

    You’ll need a main directory that holds the code for both the frontend (React.js) and backend (Laravel). Open your terminal, navigate to a path of your choice, and create a directory chat-app-react-laravel-ably by running the following command:

    mkdir chat-app-react-laravel-ably

    In the chat-app-react-laravel-ably directory, you’ll install both Laravel and React.js projects.

    Setting up the backend

    Start by building the backend part of the chat app.

    Then in your terminal, execute the following command to create a Laravel project in the backend directory:

    composer create-project laravel/laravel backend
    

    Next, once the above command has been executed, start your Laravel project by running the following commands in your terminal:

    cd backend
    php artisan serve
    

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

    Laravel app home page

    Setting up broadcasting

    You’ll need to register the App\Providers\BroadcastServiceProvider before broadcasting any events. To do so, open the config/app.php file and then uncomment the BroadcastServiceProvider in the providers array:

    'providers' => [
    ...
    App\Providers\BroadcastServiceProvider::class,
    ...
    ]
    

    This BroadcastServiceProvider class contains the necessary code for registering the broadcast authorization routes and callbacks.

    Next, since you want to broadcast your events using Ably, install the Ably PHP SDK by running the following command in your terminal:

    composer require ably/ably-php
    

    You should also configure your Ably credentials in the config/broadcasting.php file. An example Ably configuration is already included in this file, allowing you to quickly specify your key.

    Then add the ABLY_KEY environment variable to the .env file and replace your-ably-key with your registered Ably app’s key from the Ably dashboard:

    ABLY_KEY=your-ably-key
    

    Lastly, change the broadcast driver (BROADCAST_DRIVER) to ably in the .env file:

    BROADCAST_DRIVER=ably
    

    Your Laravel project is now ready to start broadcasting events.

    Broadcasting events in Laravel with WebSockets

    Before writing the code for broadcasting events to your React.js frontend, it is important to know how Laravel’s broadcasting works.

    Laravel’s event broadcasting allows you to broadcast your server-side events to your client-side application using a driver-based approach to WebSocket. You can consume these events on the client-side using the Laravel Echo npm package.

    Events are broadcast over channels. Your application can subscribe to these channels with/without authentication or authorization, depending on whether the channels are public or private.

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

    php artisan make:event MessageEvent
    

    This command will create a MessageEvent.php file in the app/Events directory.

    Next, open the app/Events/MessageEvent.php file and replace its content with the following code:

    <?php
    namespace App\Events;
    use Illuminate\Broadcasting\Channel;
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Broadcasting\PresenceChannel;
    use Illuminate\Broadcasting\PrivateChannel;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    use Illuminate\Foundation\Events\Dispatchable;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Support\Str;
    // 1
    class MessageEvent implements ShouldBroadcast
    {
    use Dispatchable, InteractsWithSockets, SerializesModels;
    private $user, $message;
    // 2
    public function __construct($user, $message)
    {
    $this->user = $user;
    $this->message = $message;
    }
    // 3
    public function broadcastWith()
    {
    return [
    'id' => Str::orderedUuid(),
    'user' => $this->user,
    'message' => $this->message,
    'createdAt' => now()->toDateTimeString(),
    ];
    }
    // 4
    public function broadcastAs()
    {
    return 'message.new';
    }
    // 5
    public function broadcastOn()
    {
    return new Channel('public.room');
    }
    }
    

    Here are the specifics in the above code:

    1. You define a  MessageEvent  class that implements the `ShouldBroadcast` interface.

    2. You define a constructor (__construct) for the MessageEvent class to which you’ll pass the user ($user) and message  $message) variables when creating the MessageEvent event object.

    3. You define a broadcastWith method that returns the array of data related to the message you want to broadcast. By default, all an event class’s public properties are automatically serialized and broadcast as the event’s payload. So this method gives you more control over the data you broadcast over the event’s channels.

    4. You specify the broadcast name as message.new by defining a broadcastAs method. This method is not required because, by default, Laravel broadcasts the event using the event’s class name.

    5. You define a broadcastOn method that is responsible for returning the channels on which the event should broadcast. In this case, the event would be broadcast on a public channel (Channel) named public.room. If you want to broadcast an event on a private channel, you use PrivateChannel instead of Channel.

    Receiving requests from the React.js frontend

    In this chat app project, the frontend will send a POST request to the Laravel backend. Upon receiving the request, Laravel will launch the MessageEvent event and broadcast the event using the Ably-specified broadcast driver.

    To receive API requests from the frontend, open the routes/api.php file and replace its content with the following code:

    <?php
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Route;
    use App\Events\MessageEvent;
    // 1
    Route::post('new-message', function (Request $request) {
    // 2
    event(new MessageEvent($request->user, $request->message));
    return 'ok';
    });
    

    In the above code, here are the specifics:

    1. You define an API route (`new-message`) to which you would be able to send POST requests. This API route will be available at localhost:8000/api/new-message].

    2. You fire the MessageEvent event by passing the user ($request->user) and message ($request->message) variables as arguments.

    Finally, to check if the code is working correctly so far, try sending a POST request to the localhost:8000/api/new-message endpoint using Postman or any other tool and sending the user and message as a payload. You should receive an ok response. If you do not receive an ok response, please carefully review the steps of the tutorial to ensure that you have followed them correctly.

    That’s it for the Laravel part of the project. Next, you need to set up a React.js app to listen to the events being broadcast by the Laravel backend.

    Setting up the React.js frontend

    Now that you have completely set up your Laravel project, it’s time to build the React.js frontend app.

    Since your current terminal window is serving the Laravel project, open another terminal window and execute the following command from the project’s root directory (chat-app-react-laravel-ably) to create a React.js project using the famous create-react-app:

    npx create-react-app frontend
    

    Finally, once the installation is complete, navigate to the frontend directory and start the React.js development server by running the following commands in your terminal:

    cd frontend
    npm start
    

    This command will start the development server on port 3000 and take you to localhost:3000. The first view of the React.js website will look like this:

    React.js website home page

    Installing frontend packages

    You will need to install the following packages to create the frontend of the chat app:

    - axios: This package allows you to make HTTP requests in your client-side application.

    - Laravel Echo: This JavaScript library allows you to subscribe to channels and listen to the events being broadcast by your server-side broadcasting driver.

    - pusher-js: You might think “why do I need pusher-js when I am using Ably to broadcast my events?” This is because Ably includes a Pusher protocol adapter that lets you use the Pusher protocol when listening for events in your client-side application.

    Shut down the React.js development server by pressing Control-C and then execute the following command to install the required modules for your React.js app:

    npm install axios laravel-echo pusher-js
    

    Once the installation is complete, add the REACT_APP_MIX_ABLY_PUBLIC_KEY environment variable to the .env file. Your Ably public key is the portion of your Ably key that occurs before the : character:

    REACT_APP_MIX_ABLY_PUBLIC_KEY=<your-ably-public-key>
    REACT_APP_API_BASE_URL=http://localhost:8000/api
    

    In the above configuration, you have specified REACT_APP_MIX_ABLY_PUBLIC_KEY and REACT_APP_API_BASE_URL. It is important to prefix your environment variables with REACT_APP_ so that they can be used by create-react-app.

    Setting up Laravel Echo

    You must set up Laravel Echo before it can help you subscribe to channels and listen for events. Open the src/App.js file and input the following code:

    // 1
    import PublicMessagesPage from './components/PublicMessagesPage';
    // 2
    export default function App() {
    return <PublicMessagesPage />;
    }
    

    In the above code, this is what’s happening:

    1. You import the  PublicMessagesPage  component.

    2. You render the PublicMessagesPage component in the `App` component.

    Building the frontend UI

    You will now build the public chat home page and message box component that comprise the app’s frontend UI.

    Building the public chat home page

    To begin, create a components directory in the src directory. Then, in the components directory, create a PublicMessagesPage.js file and add the following code to it:

    // 1
    import React, { useState, useEffect } from 'react';
    import Axios from 'axios';
    import Echo from 'laravel-echo';
    import Pusher from 'pusher-js';
    import Messagebox from './Messagebox';
    // 2
    export default function PublicMessagesPage() {
    // 3
    const [user, setUser] = useState('');
    const [message, setMessage] = useState('');
    const [messages, setMessages] = useState([]);
    async function handleSendMessage(e) {
    // TODO
    }
    // 4
    return (
    <div>
    <div>
    <div>
    <h1>Public Space</h1>
    <p>Post your random thoughts for the world to see</p>
    </div>
    <div>
    {messages.map((message) => (
    <Messagebox key={message.id} message={message} />
    ))}
    </div>
    <div>
    <form onSubmit={(e) => handleSendMessage(e)}>
    <input
    type="text"
    placeholder="Set your username"
    value={user}
    onChange={(e) => setUser(e.target.value)}
    required
    />
    <div>
    <input
    type="text"
    placeholder="Type your message..."
    value={message}
    onChange={(e) => setMessage(e.target.value)}
    required
    />
    <button onClick={(e) => handleSendMessage(e)}>Send</button>
    </div>
    </form>
    </div>
    </div>
    </div>
    );
    }
    

    This is what’s happening in the above code:

    1. You import the required NPM packages.

    2. You define the PublicMessagesPage functional component.

    3. You define the state variables for PublicMessagesPage component using useState React hook:

    -   user  stores the current user’s name

    - message  stores the currently typed message

    - messages  stores all the messages sent and received in the present session

    4. You return the JSX template for the Public Messages Page.

    > Note: The styling part of the app was skipped because understanding the functionality is more important for now and styling the app can be done according to your preference.

    Next, you need to implement the Messagebox component.

    Creating the message box component

    To start, in the components directory, create a Messagebox.js file and add the following code to it:

    // 1
    export default function Messagebox({ message }) {
    const formatDate = (value) => {
    if (!value) return '';
    return new Date(value).toLocalTimeString();
    };
    // 2
    return (
    <div>
    <div>
    <p>
    <b>{message.user}</b>
    </p>
    <p>{message.message}</p>
    <p>{formatDate(message.createdAt)}</p>
    </div>
    </div>
    );
    }
    

    Here are the above code’s specifics:

    1. You define a stateless functional component  Messagebox  and pass the  message   object as its parameter.

    2. You return the HTML template for the  Messagebox  component.

    At this point, save your progress and restart the React.js development server by running the following command in your terminal:

    npm start
    

    Visit localhost:3000, and based on the way you’ve styled your app, you’ll get something like this:

    Chat app home page

    Sending messages

    With the frontend UI established, the next step is to enable message sending. In the src/components/PublicMessagesPage.js file, you need to implement the handleSendMessage method. So start by updating the handleSendMessage function by adding the following code:

    async handleSendMessage(e) {
    // 1
    e.preventDefault();
    // 2
    if (!user) {
    alert('Please add your username');
    return;
    }
    // 3
    if (!message) {
    alert('Please add a message');
    return;
    }
    try {
    // 4
    await Axios.post('/new-message', {
    user: user,
    message: message,
    });
    } catch (error) {
    console.error(error);
    }
    }
    

    Here are the specifics for the above code:

    1. You use the `e.preventDefault()` method to prevent the page from reloading as the form is submitted.

    2. You validate whether the user state variable is empty. If empty, you alert the user to add their username.

    3. You also validate whether the message state variable is empty. If empty, you alert the user to add a message.

    4. You make a POST request (Axios.post) to the /new-message API endpoint after the validation is successful and send the user and message state variables as a payload.

    Once a POST request is made to the Laravel endpoint, the MessageEvent event is fired. So next, you need to listen to this event in your React.js app.

    Listening to events

    To listen to the events, add the useEffect React hook to the PublicMessagesPage component in the src/components/PublicMessagesPage.js file:

    // 1
    useEffect(() => {
    // 2
    Axios.defaults.baseURL = process.env.REACT_APP_API_BASE_URL;
    // 3
    const echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.REACT_APP_MIX_ABLY_PUBLIC_KEY,
    wsHost: 'realtime-pusher.ably.io',
    wsPort: 443,
    disableStats: true,
    encrypted: true,
    });
    // 4
    echo
    .channel('public.room')
    .subscribed(() => {
    console.log('You are subscribed');
    })
    // 5
    .listen('.message.new', (data) => {
    // 6
    setMessages((oldMessages) => [...oldMessages, data]);
    setMessage('');
    });
    }, []);
    

    In the above code, here are the specifics:

    1. You define the  useEffect React hook that is called immediately after the `PublicMessagesPage` component is mounted.

    2. You define the base URL (Axios.defaults.baseURL) for the Axios package on which it makes the HTTP requests to the backend API.

    3. You create a new Echo instance and subscribe to the public.room channel on the backend.

    4. You subscribe to the public channel (public.room) on which the events are being broadcast.

    5. You listen to the message.new event and pass the callback function that receives the data sent as the event response. Since you have defined a custom broadcast name using the broadcastAs method, you need to add the . character before the event name. This addition instructs the Echo not to prepend the application’s namespace to the event name.

    5. You update the messages state variable by adding the new message payload (data) to the existing messages (oldMessages) in the state.

    Finally, save your progress and reload the React.js development server. You’ll now be able to send messages to the public chat room:

    Sending messages in chat app‌‌

    Testing the app

    To test your chat app, open the localhost:3000 in at least two browser tabs or windows and try sending messages by setting different usernames. Here’s how the app will behave:

    Message sending between two users

    Conclusion

    That’s it! In this tutorial, you learned how to create a real-time chat app using React.js, Laravel, and Ably. You also tested if your app was functioning correctly. Depending on your requirements, you can discover how to add more features to your chat app in the awesome Ably documentation. It’s exciting to hear about how you’ll use Ably in your applications.

    The entire source code for this tutorial is available in this GitHub repository.

    Ravgeet Dhillon

    Ravgeet Dhillon

    Ravgeet is a Guest Author at the Ably Blog. He is a Full-Stack Developer and Technical Content Writer based in India ??. Currently, he is working as a Software Engineer at CloudAnswers.

    Read More of Developers