Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 16, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 16, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 16, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 16, 2025

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025

      Minecraft licensing robbed us of this controversial NFL schedule release video

      May 16, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      The power of generators

      May 16, 2025
      Recent

      The power of generators

      May 16, 2025

      Simplify Factory Associations with Laravel’s UseFactory Attribute

      May 16, 2025

      This Week in Laravel: React Native, PhpStorm Junie, and more

      May 16, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025
      Recent

      Microsoft has closed its “Experience Center” store in Sydney, Australia — as it ramps up a continued digital growth campaign

      May 16, 2025

      Bing Search APIs to be “decommissioned completely” as Microsoft urges developers to use its Azure agentic AI alternative

      May 16, 2025

      Microsoft might kill the Surface Laptop Studio as production is quietly halted

      May 16, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»Building a real-time chat app using Laravel Reverb and Vue

    Building a real-time chat app using Laravel Reverb and Vue

    November 4, 2024

    Laravel Reverb is a real-time WebSocket framework that broadcasts events from Laravel to the frontend. It allows for real-time data synchronization across connected clients without page reloads. Vue is a JavaScript framework that allows for a reactive, component-based frontend experience.

    Building A Real-Time Chat App Using Laravel Reverb And Vue

    In this guide, we’ll explore building a fully functional, real-time chat application using Laravel Reverb’s backend and Vue’s reactive frontend. As usual, the final, functioning code is available in this GitHub repository.

    Setting up Laravel and Vue prerequisites

    Before starting the development, you must set up an environment consisting of two components: the Laravel one, which is PHP-based, and the Vue/Node component.

    • PHP: Version 8.2 or above (run php -v to check the version)
    • Composer (run composer to check that it exists)
    • Node.js: Version 20 or above (run node -v to check the version)

    In the following image, you can see the output of the command above on my Windows machine:

    Command Output On The Windows Machine

    For the database, we will use SQLite, so be sure to activate it in your php.ini file. Once you have met all the basic prerequisites, you can create a Laravel project by using the following:

     > composer create-project laravel/laravel:^11.0 laravel_chat_demo
    

    Once you get your project root dir ready to start the development, you might need to install some more requirements; this process depends on your specific environment but, in general, try to make your composer command happy (🙂), if it complains (or just warns you) that a package is missing, install that package (Google is your friend).

    Once everything is in place, you should be able to run the following:

     > php artisan serve
    

    This will fire up your development environment.

    Building the data model for chat messages

    Now that the development environment is (hopefully) fine, we can concentrate on the stimulating part of the development.

    We are writing a chat web application so we can expect to handle messages. With the following command, we will get a brand new class in the /app/Models that will represent the messages exchanged in our chat:

     > php artisan make:model -m Message
    

    Right now, the class is empty. We will specialize it with data and functionalities, so replace the file Message.php with the following code:

    <?php
    
    namespace AppModels;
    
    use IlluminateDatabaseEloquentFactoriesHasFactory;
    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentRelationsBelongsTo;
    
    class Message extends Model
    {
        use HasFactory;
    
        public $table = 'messages';
        protected $fillable = ['id', 'user_id', 'text'];
    
        public function user(): BelongsTo
        {
            return $this->belongsTo(User::class, 'user_id');
        }
    
        public function getTimeAttribute(): string
        {
            return date(
                "d M Y, H:i:s",
                strtotime($this->attributes['created_at'])
            );
        }
    }
    

    The code above describes a message in our system: messages will be stored in the table named messages; each message comprises three fields: id, user_id, and text. The rest of the code does two things: it creates a function that returns the user associated with the message by using the BelongsTo trait, and it defines a function that formats the field created_at in a more human-readable date and time format. It will be used on the top of each message in the chat box.

    The model we just wrote will instruct Eloquent (the Laravel ORM) on how to interact with data on our database, the next step is to actually create the table on the database; to do so we need a migration, that is, a fragment of code that will create the table:

     > php artisan make:migration create_message_table
    

    This will create an empty migration file in databasemigrations whose name will be something like <date and time>_create_message_table.php. The file contains two key methods: up and down. The up method introduces changes to your database schema, such as creating new tables, adding columns, or establishing indexes.

    Conversely, the down method’s role is to undo or reverse the modifications made by the corresponding up method, effectively allowing you to roll back changes if needed. We specialize this file with the following code:

    <?php
    
    use IlluminateDatabaseMigrationsMigration;
    use IlluminateDatabaseSchemaBlueprint;
    use IlluminateSupportFacadesSchema;
    
    return new class extends Migration {
        public function up(): void {
            Schema::create('messages', function (Blueprint $table) {
                $table->id();
                $table->timestamps();
    
                $table->foreignId('user_id')->constrained();
                $table->text('text')->nullable();
            });
        }
    
        public function down(): void {
            Schema::dropIfExists('messages');
        }
    };
    

    This migration defines a new messages table with some fields: an auto-incrementing ID as the primary key, a user_id that references the users table to link messages with their senders, and a text field to store the message content.

    The table also incorporates timestamp columns to track when each message is created and modified automatically; timestamps will create the two fields created_at and updated_at. As a precautionary measure, the operation includes a reversal method that can remove the messages table if needed, allowing for easy rollback of these changes.

    At this point, let’s run the following:

     > php artisan migrate:fresh
    

    This will create a new table in the database. If you want to check that it was successful, simply open the databasedatabase.sqlite file.

    Adding user registration and login

    Now that we have room for data, it is time to create the frontend. When developing the user interface for a Laravel application, you have two main options: the first involves using PHP to construct your frontend. In contrast, the second uses JavaScript frameworks like Vue or React.

    We will use Vue in this article. This will also allow developers to take advantage of the huge packages ecosystem and tools available via npm. First, we need to install the appropriate package:

     > composer require laravel/ui
    

    Once the laravel/ui package has been installed, you may install the frontend scaffolding using the artisan command: the following command generates the UI for handling the registration and the login to our web app; just consider how complex this task can be if you should write this from scratch and how incredibly easy it is handled in Laravel:

     > php artisan ui vue --auth
    

    At this point, we have the two halves of the project in place: the PHP is already running with the PHP artisan serve command; now it is the moment to run the JavaScript-based part with:

     > npm install
    

    To have both the parts running, open two shell windows; one running the artisan command, and the other with the following command:

     > npm run dev
    

    This command will keep the frontend running and reload it every time we modify it.

    At the end of this process, without writing a single line of code, we have a fully functional website with the possibility of handling user authentication. Go to http://127.0.0.1:8000/register to register a new user and log in to the website:

    User Login Page

    Configuring routes for the chat message API

    Now, we need to add routes for the different APIs we are going to host:

    • /home for the home page – this should already be present
    • /message, a POST HTTP method for adding a new message
    • /messages to GET all the existing messages

    We will modify the /routes/web.php as follows:

    <?php
    
    use IlluminateSupportFacadesAuth;
    use IlluminateSupportFacadesRoute;
    use AppHttpControllersHomeController;
    
    Route::get('/', function () { return view('welcome'); });
    
    Auth::routes();
    
    Route::get('/home', [HomeController::class, 'index'])
        ->name('home');
    Route::get('/messages', [HomeController::class, 'messages'])
        ->name('messages');
    Route::post('/message', [HomeController::class, 'message'])
        ->name('message');
    

    Now, it is time to write the HomeController that will implement the APIs we described above:

    <?php
    
    namespace AppHttpControllers;
    
    use AppJobsSendMessage;
    use AppModelsMessage;
    use AppModelsUser;
    use IlluminateHttpJsonResponse;
    use IlluminateHttpRequest;
    
    class HomeController extends Controller
    {
        public function __construct()
        {
            $this->middleware('auth');
        }
    
        public function index()
        {
            $user = User::where('id', auth()->id())->select([
                'id',
                'name',
                'email',
            ])->first();
    
            return view('home', [
                'user' => $user,
            ]);
        }
    
        public function messages(): JsonResponse
        {
            $messages = Message::with('user')->get()->append('time');
    
            return response()->json($messages);
        }
    
        public function message(Request $request): JsonResponse
        {
            $message = Message::create([
                'user_id' => auth()->id(),
                'text' => $request->get('text'),
            ]);
            SendMessage::dispatch($message);
    
            return response()->json([
                'success' => true,
                'message' => "Message created and job dispatched.",
            ]);
        }
    }
    

    Here you can see the logic behind the APIs described above:

    • In the /home method, we retrieve the logged-in user’s information from the database using the User model and pass it to the view
    • In the /messages method, we fetch all messages from the database via the Message model, include the related user data, add the time field (using an accessor) to each message, and send the complete set to the view
    • In the /message method, we’ll create a new message in the database using the Message model and dispatch the SendMessage queue job

    When everything is set, we can install and configure Laravel events and queue jobs to host the exchange and synchronization of messages.

    Configuring Laravel event and queue job

    Laravel’s event and queue job systems provide powerful tools for handling asynchronous tasks and decoupling various parts of an application.

    Events allow you to define and broadcast specific actions or changes within your application, which listeners can then respond to. Queue jobs enable you to offload time-consuming tasks, such as sending emails or processing large datasets, to background workers, ensuring that your application remains responsive and efficient.

    Together, these features enhance scalability and improve the overall user experience by managing processes in the background. We will use both: the event is essentially a data container that holds the message, and the QueueListener handles the number of messages waiting to be dispatched.

    With the following command, we generate the Event class in the /app/Events directory:

     > php artisan make:event GotMessage
    

    Then we have to implement two things: the constructor that will describe what is the payload of this event, and the broadcastOn() method to specify on which channel these events will be broadcasted:

    <?php
    
    namespace AppEvents;
    
    use IlluminateBroadcastingInteractsWithSockets;
    use IlluminateBroadcastingPrivateChannel;
    use IlluminateContractsBroadcastingShouldBroadcast;
    use IlluminateFoundationEventsDispatchable;
    use IlluminateQueueSerializesModels;
    
    class GotMessage implements ShouldBroadcast {
        use Dispatchable, InteractsWithSockets, SerializesModels;
    
        public function __construct() {
        }
    
        public function broadcastOn() {
            return  new PrivateChannel("channel_for_everyone");
        }
    }
    

    The name of the channel (“channel_for_everyone“) will be used in the file describing the WebSocket (see below) that will represent the communication channel between each instance of the chat client and the server. Note here that the constructor takes no parameters and there is no reference to the messages: the idea is that this event will be broadcasted once a new message has been sent, once the client receives it, they will just request the updated list of messages to the server using the services we implemented before.

    To generate the QueueListener, we use the following:

     > php artisan make:job SendMessage
    

    This command generates the SendMessage.php file in the /app/Jobs directory. This file instructs the Laravel framework on how to handle newly created instances of the GotMessage event that we defined earlier:

    <?php
    
    namespace AppJobs;
    
    use AppEventsGotMessage;
    use AppModelsMessage;
    use IlluminateBusQueueable;
    use IlluminateContractsQueueShouldQueue;
    use IlluminateFoundationBusDispatchable;
    use IlluminateQueueInteractsWithQueue;
    use IlluminateQueueSerializesModels;
    
    class SendMessage implements ShouldQueue {
        use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    
        public function __construct(public Message $message) {
            //
        }
    
        public function handle(): void {
            GotMessage::dispatch([
                'id' => $this->message->id,
                'user_id' => $this->message->user_id,
                'text' => $this->message->text,
                'time' => $this->message->time,
            ]);
        }
    }
    

    The handle() method dispatches the GotMessage event with details such as the message id, user_id, text, and timestamp. This job is designed to run in the background asynchronously processing events as long as they are enqueued on the channel, enabling efficient handling of message-sending tasks in the background. As you can see, there is a public $message property as a constructor argument; this helps implement the queue process more efficiently (see the documentation for additional details)

    With all other components in place, we now just need to add the final PHP element for the project: the WebSocket.

    Adding Laravel Reverb

    Laravel Reverb brings a real-time WebSocket framework that allows you to send events from your Laravel application to the frontend using WebSockets. With Reverb, it is possible to broadcast the events we defined above, reflected on each client connected, without requiring a page reload. As usual, adding this quite complex feature is addressed with one simple command:

     > php artisan install:broadcasting
    

    By accepting the default option, it will install both the PHP part for the backend and the Node dependencies to be used on the frontend.

    Running this command will make several changes to your project directory: it will add a new section to the .env file for Reverb, create a reverb.php file in /config to read these new fields, and, most importantly, add a channels.php file in /routes. In this channels.php file, we’ll configure Reverb to create the channel_for_everyone channel by adding the following code:

    Broadcast::channel('channel_for_everyone', function ($user) {
        return true;
    });
    

    At this point, everything is in place on the backend. Now we can focus on the frontend.

    Building the Vue frontend

    In this section, we will design a simple frontend, focusing on functionality and integration, without any styling, font customization, etc.

    The first step is to set up the Vue environment:

    > npm install vue vue-router @vitejs/plugin-vue

    Now we can focus on three files to integrate a Vue template in Laravel. The first one is resources/js/app.js, which will instantiate the Vue component that contains our app:

    import './bootstrap';
    import App from './App.vue'
    import { createApp } from 'vue';
    
    const app = createApp({});
    
    app.component('app', App);
    
    app.mount("#app");
    

    The code is simple: App.vue is our Vue template and we just associate it with the div whose id is #app in the blade file (see the next code snippet) that will render our web app. The next file is resources/js/App.vue, which contains the template:

    <script setup>
    const props = defineProps({
        isAuth: {
            type: Boolean,
            default: false
        },
        user: {
            type: [Object, Array],
            required: false
        }
    })
    </script>
    <template>
        <h1>Hello, {{ user.name }}</h1>
    </template>
    

    The code does two things: it defines the props for Vue to host the values coming from Laravel, and then displays a simple message. As you can see, we just received the current authenticated user and show their name.

    The last file is the blade that will glue together the Vue component and the data we want to feed to it. Let’s just copy the following code in the resourcesviewswelcome.blade.php file:

    <!DOCTYPE html>
    
    <head> 
     <title>Laravel + Vue Chat</title>
     @vite(['resources/js/app.js'])
    </head>
    
    <body>
     <div class="min-h-screen bg-gray-100" id="app">
      <header >
       @if (Route::has('login'))
        <nav>
         @auth
          <div id="app">
           <app :is-auth="{{ json_encode(auth()->check()) }}"
                :user="{{ auth()->check() ? auth()->user() : 'null' }}">>
           </app>
        { ... Logout code .. }
          </div>
         @else
          <a href="{{ route('login') }}"> Log in </a>
          @if (Route::has('register'))
           <a href="{{ route('register') }}"> Register </a>
          @endif
         @endauth
         </nav>
        @endif
       </header>
      </div>
    </body>
    
    </html>
    

    In the code above, we first include the app.js file to access the Vue component we just created. Then it renders the Login/Register options if the user is not logged in or passes the is-auth and user variables to the props we defined in the resources/js/App.vue file above.

    In the chunk of code above, the Logout code is missing. Check the repository for additional details.

    To verify that everything works, you will need to keep running four server components in four different shell windows:

    • Build frontend assets for the Vue components:
     > npm run build
    • Start listening to the Laravel events:
     > php artisan queue:listen
    • Start the Reverb WebSocket server:
     > php artisan reverb:start
    • Start the Laravel server:
     > php artisan serve
    

    Open your browser, and go to http://127.0.0.1:8000/; our brand-new chat app will appear. In the image below, you can see the four services running one beside the other:

    The Four Services Running One Beside The Other

    If everything is in place, you can show in your browser the following message (keep in mind that the name will be different! Unless, of course, you sign in with the name Rosario 🙂):

    Greeting Message In The App Login Page

    Before finalizing the application, I’ll give you two useful tips if you incur problems during the development:

    • Have a look at the file storagelogslaravel.log; this is where you can write logs with the Log facility and where each component of your system will complain if something is not working
    • Launch the reverb:start with php artisan reverb:start --debug to have more debug messages

    Now it is time to complete the frontend with all the required pieces of a chat web app: a text box of new messages and a list of messages:

    <script>
    import axios from 'axios';
    
    export default {
        data() {
            return {
                messages: [],
                newMessage: "",
            };
        },
        methods: {
            async postMessage(text) {
                try {
                    await axios.post(`/message`, {
                        text,
                    });
                    // After posting, retrieve messages to include the new one
                    this.getMessages();
                } catch (err) {
                    console.log(err.message);
                }
            },
            async getMessages() {
                try {
                    const response = await axios.get('/messages');
                    this.messages = response.data;
                    // Scroll to the bottom after messages are updated
                    this.scrollToBottom();
                } catch (err) {
                    console.log(err.message);
                }
            },
            sendMessage() {
                if (this.newMessage.trim() !== "") {
                    this.postMessage(this.newMessage.trim());
                    this.newMessage = "";
                } else {
                    return;
                }
            },
            scrollToBottom() {
                this.$nextTick(() => {
                    const messageList = document.getElementById('messagelist');
                    if (messageList) {
                        messageList.scrollTop = messageList.scrollHeight;
                    }
                });
            }
        },
        created() {
            this.getMessages();
    
            window.Echo.private("channel_for_everyone")
                .listen('GotMessage', (e) => {
                    this.getMessages();
                });
        },
    };
    </script>
    
    <template>
        <div class="container">
            <div class="chat-box" id="messagelist">
                <div v-for="(message, index) in messages" :key="index" class="message">
                    <strong>{{ message.user.name }}:</strong> {{ message.text }}
                    <small class="text-muted float-right">{{ message.time }}</small>
                </div>
            </div>
            <div class="input-area">
                <input v-model="newMessage" @keyup.enter="sendMessage" type="text"
                    placeholder="Type your message here..." />
                <button @click="sendMessage">Send</button>
            </div>
        </div>
    </template>
    
    <style scoped>
    .chat-box {
        border: 1px solid #ccc;
        padding: 10px;
        max-height: 300px;
        overflow-y: auto;
    }
    
    .message {
        margin-bottom: 10px;
    }
    </style>
    

    The code, as you can see, is a bit longer but it is fairly simple:

    • The data method defines the two global objects we manipulate here, the messages array that contains the messages already sent to the chat and the newMessage that contains the new text message sent by the user
    • The four methods are:
      • postMessage to do an HTTP POST to the /message API we defined above to send a message text to the backend
      • getMessages, which invokes the /getMessages API to get the messages in the DB
      • sendMessage, which invokes the async method postMessage and cleans the textbox once the message has been POSTed to the backend
      • scrollToBottom, which just scrolls the list of messages to the last message received by the backend
    • created is the method that runs when the component is first created. It updates the list of existing messages by invoking getMessage and then instructs the Echo service (which listens on the WebSocket) to subscribe to new events on the channel_for_everyone channel we defined above

    After the methods’ definition, you can see the template of the Vue component with very simple HTML for the message box the text box for new messages, and a little CSS to style everything.

    The following image shows the final UI of the chat web app running in the browser:

    Final UI Of The Chat Web App

    Conclusion

    In this project, we built a real-time chat app using Laravel Reverb and Vue, demonstrating how to integrate Laravel’s event-driven backend with Vue’s reactive frontend. Use the information learned here to develop scalable, interactive web applications. Explore the final code on GitHub to see Laravel Reverb and Vue in action.

    The post Building a real-time chat app using Laravel Reverb and Vue appeared first on LogRocket Blog.

    Source: Read More

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleCritical Flaws in Ollama AI Framework Could Enable DoS, Model Theft, and Poisoning
    Next Article Aerospike Kubernetes Operator 3.4 adds better backup and scalability capabilities

    Related Posts

    Machine Learning

    Salesforce AI Releases BLIP3-o: A Fully Open-Source Unified Multimodal Model Built with CLIP Embeddings and Flow Matching for Image Understanding and Generation

    May 16, 2025
    Security

    Nmap 7.96 Launches with Lightning-Fast DNS and 612 Scripts

    May 16, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    Yubico Warns of 2FA Security Flaw in pam-u2f for Linux and macOS Users

    Development

    MSP Best Practices: Server Maintenance Checklist

    News & Updates

    Here are the 7 coolest tech products I saw at my very first CES

    News & Updates

    The ROI of Security Investments: How Cybersecurity Leaders Prove It

    Development

    Highlights

    Development

    WhatsApp now lets 32 people join a video call on all platforms

    June 14, 2024

    WhatsApp is slowly turning into a decent Zoom or Google Meet competitor. Meta’s social app…

    Researchers Deep Dive into UNC3886 Actors’ Cyberespionage Realm

    June 19, 2024

    Accelerate IaC troubleshooting with Amazon Bedrock Agents

    February 25, 2025

    Top Factors to Consider When Choosing the Right AI Service Provider

    May 2, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.