Building a Chat Room with Laravel, and Vue.js

7 min read

Being able to communicate with a group of people over live chat is one of the most powerful features of modern messaging. Groups of people can have live general discussions across the board as though it were a one-to-one chat, which makes sharing general information fast and easy. In this tutorial, we’ll build a chatroom application using Laravel, Vue.js, and Stream Chat.

Chimezie E.
Chimezie E.
Published October 31, 2019 Updated June 20, 2021

Being able to communicate with a group of people in real-time over chat is one of the most powerful features of modern messaging. Real-time messaging with groups of people can have live general discussions across the board as though it were a one-to-one chat, which makes sharing general information fast and easy. In this tutorial, we’ll build a chatroom application using Laravel, Vue.js, and Stream Chat.

Prerequisites

To follow along with this tutorial, a basic understanding of Laravel, Vue.js, and JavaScript is required. Also, this tutorial assumes you have the Laravel installer installed on your computer. Lastly, a Stream Chat account is required. You can sign up for a free 14-days trial (no credit card required).

What we’ll be building

In this tutorial, we’ll be building a chat room where users can join and chat with other users in real-time. Users will be automatically added to the chatroom at the point of registering on our application. Below is a quick demo of what our finished application will look like:

Getting our Stream Chat keys

To start using the Stream Chat API, we need to have our Stream Chat API keys. Create an account or log in if you already have an account. Once you’re logged in, you can save the keys from the app that's automatically created for you. Or, if you prefer, on your dashboard, create a new app by clicking the Create App button.

Image shows a new app being created in the stream dashboard

Once that is done, you should see an API Key, Secret, and App ID of your newly created application.

Image shows an app's credentials in the stream dashboard

Take note of these keys as we’ll be needing them shortly.

Getting Started

To get started, we’ll be using the Laravel installer to create a new Laravel application. Run the following command to create a new Laravel application:

laravel new laravel-stream-chatroom && cd laravel-stream-chatroom

Once the application is created, we need to install the NPM dependencies (because Vue.js and Bootstrap come pre-packed as an NPM dependency). In your terminal run:

[https://gist.github.com/ammezie/ed72b3580eb3217539aed9a880b89e04]

For this tutorial, we'll be concerned with the resources/js directory, which is where Vue.js will be instantiated. Here is a tutorial on building a real-time chat app with Vue.

Now, we can start building our application.

Building the backend

As already mentioned, the backend will be built with Laravel, since we already have a new Laravel project, let’s start fleshing it out. For our chatroom application, we want users to register before they are granted access to the chatroom, so we’ll start by adding authentication. Laravel makes this seamless. In Laravel 6, the authentication scaffolding is extracted to a separate package, so we need to install the following package:

composer require laravel/ui --dev

Once that’s installed, we can run the command below to create the authentication scaffolding:

php artisan ui vue --auth

Now, we can run the default migrations that Laravel created for us:

php artisan migrate

Note: Make sure you have set up your database before running the migrations.

Let’s add our Stream Chat keys to .env:

// .env

MIX_STREAM_API_KEY=YOUR_STREAM_API_KEY
MIX_STREAM_API_SECRET=YOUR_STREAM_API_SECRET

Remember to update the YOUR_STREAM_API_KEY and YOUR_STREAM_API_SECRET placeholders with your actual API details. To be able to access these environment variables in our frontend as well, we need to prefix them with MIX_.

Let’s start our application to make sure everything is working as expected:

php artisan serve

Now, we have Laravel default authentication in place. Let’s make some modifications to suit our application needs. Add the following code inside app/Http/Controllers/Auth/RegisterController.php:

// app/Http/Controllers/Auth/RegisterController.php

// first import StreamChat PHP client
use GetStream\StreamChat\Client as StreamClient;

protected function registered($user){
    $client = new StreamClient(env("MIX_STREAM_API_KEY"), env("MIX_STREAM_API_SECRET"), null, null, 9);

    $username = explode('@', $user->email)[0];

    // create the user on Stream Chat
    $client->updateUser([
        'id' => $username,
        'name' => $user->name,
    ]);

    // create channel
    $channel = $client->Channel("messaging", "chatroom");

    // channel is created by `admin` user
    $channel->create('admin');

    // then add the newly registered user as a member
    $channel->addMembers([$username]);
}

The registered() function gets called once a user has registered within our application.

Building your own app? Get early access to our Livestream or Video Calling API and launch in days!

In addition to creating users on our end, we want to also create the users in Stream Chat, so we use the updateUser() function on the StreamClient instance.

We will need to extract a username from the user's email address, which we then use as the users id on Stream Chat and name as the user's name.

Also, we create a channel on Stream Chat of the type messaging, which we call chatroom. Using the create(), we set the creator of the channel to an admin user. Lastly, we add the newly registered user as a member of the channel.

Now, whenever a user registers within our application, they’ll automatically be added to the channel.

Before we move on, let’s install the Stream Chat PHP client:

composer require get-stream/stream-chat

Our final task on our backend will be to send an authentication token from our backend to the frontend. This is what Stream Chat will use to authenticate the origin of the request and if the frontend is allowed to have the information.

Create a ChatController.php inside the app/Http/Controllers directory and add the following to it:

// app/http/Controllers/ChatController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GetStream\StreamChat\Client as StreamClient;

class ChatController extends Controller
{
    /**
     * Generate token for clientside use
     */
    public function generateToken(Request $request)
    {
        $client = new StreamClient(env("MIX_STREAM_API_KEY"), env("MIX_STREAM_API_SECRET"), null, null, 9);

        return response()->json([
            'token' => $client->createToken($request->user_id)
        ]);
    }
}

The generateToken() generates a token using the user’s id and sends it back to the client-side. To have access to this method, we need to create a route for it.

Open up routes/api.php and add the following to it:

// routes/api.php

Route::post('/generate-token', 'ChatController@generateToken');

Now our backend work is done. Let’s implement the functionality of our frontend to start chatting.

Building the frontend

Let’s go ahead and build the frontend of our application. Laravel supports Vue.js out of the box. So everything is already set up. To be able to use Stream Chat on our Vue.js frontend, we need to install the JavaScript client. Run the following in your terminal:

npm install stream-chat

Next, we need to create and initialize our components. Open resources/js/app.js and add the following code to it:

// resources/js/app.js
    
Vue.component('chat-room', require('./components/ChatRoom.vue').default);

We haven’t created our component yet, so technically, this will throw an error if we start up our application. Let’s build the component now.

In the resources/js/components directory, create a ChatRoom.vue file and add the following code to it:

// resources/js/components/ChatRoom.vue

<template>
  <div class="container">
    <div class="row">
      <div class="col-md-3">
        <div class="card">
          <div class="card-header">Members</div>

          <div class="card-body">
            <ul class="list-group list-group-flush">
              <li
                class="list-group-item"
                v-for="(member, id) in members"
                :key="id"
              >{{ member.user.name }}</li>
            </ul>
          </div>
        </div>
      </div>

      <div class="col-md-9">
        <div class="card">
          <div class="card-header">Chats</div>

          <div class="card-body">
            <dl v-for="message in messages" v-bind:key="message.id">
              <dt :class="{  'text-right': message.user.id === username }">{{ message.user.name }}</dt>
              <dd :class="{  'text-right': message.user.id === username }">{{ message.text }}</dd>
            </dl>

            <hr />

            <form @submit.prevent="sendMessage" method="post">
              <div class="input-group">
                <input
                  type="text"
                  v-model="newMessage"
                  class="form-control"
                  placeholder="Type your message..."
                />

                <div class="input-group-append">
                  <button class="btn btn-primary">Send</button>
                </div>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
import { StreamChat } from "stream-chat";

export default {
  name: "ChatRoom",
  props: {
    authUser: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      token: null,
      channel: null,
      members: [],
      client: null,
      messages: [],
      newMessage: ""
    };
  },
  computed: {
    username() {
      return this.authUser.email.split("@")[0];
    }
  },
  async created() {
    // gerenrate clientside token from server
    await this.getToken();
    await this.initializeStream();
    await this.initializeChannel();
  },
  methods: {
    async getToken() {
      const { data } = await axios.post("/api/generate-token", {
        user_id: this.username
      });

      this.token = data.token;
    },
    async initializeStream() {
      this.client = new StreamChat(process.env.MIX_STREAM_API_KEY, {
        timeout: 9000
      });

      await this.client.setUser(
        { id: this.username, name: this.authUser.name },
        this.token
      );
    },
    async initializeChannel() {
      this.channel = this.client.channel("messaging", "chatroom");

      const { members, messages } = await this.channel.watch();

      this.members = members;

      this.messages = messages;

      // listen for new messages
      this.channel.on("message.new", event => {
        this.messages.push({
          text: event.message.text,
          user: event.message.user
        });
      });

      // listen for when a new member is added to channel
      this.channel.on("member.added", event => {
        this.members.push(event);
      });
    },
    async sendMessage() {
      await this.channel.sendMessage({
        text: this.newMessage
      });

      this.newMessage = "";
    }
  }
};
</script>

The UI of this component is made up of two parts: a list of channel members and the chat itself. The component accepts a required prop called authUser, which is the currently authenticated user. Just as we need on the backend, we create a username computed property, which simply extracts the username from the authenticated user’s email address. We want to perform some logic as soon as the component is created, so we wrap them inside a dedicated method and add them in the created life cycle hook.

The getToken() is used to get the token from our backend, this token will be used to authenticate our connection with Stream Chat from the frontend. Next, in the initializeStream(), we instantiate a new instance of Stream Chat. Then we specify the current user with setUser(), which accepts an object of the user (here, we are only specifying the user’s id and name) and the user token.

The initializeChannel() initializes the channel we created on the backend earlier. Then we fetch the channel’s members and messages and subscribe to the channel for any future activities on it. Since we are subscribed to this channel and listening for events, we want to update the chats once messages are added. We also want to update the member list whenever a new member joins the channel. This is where Stream Chat events come into play. First, we listen for the message.new event, and update the messages array with the new message, which is passed along with the event. Next, we listen for the member.added event, and update the members array with the newly added member.

The sendMessage() is called whenever the chat form is submitted. Using the channel instance created earlier, we call a sendMessage()on it, which accepts an object of the message content. In our case, we are only sending a text. Then, we clear the form field.

Finally, we need to display the chat room component and pass to it the authUser props. In Laravel, logged in, users are redirected to the /home route, which uses the home view. Open resources/views/home.blade.php and update it as below:

// resources/views/home.blade.php

@extends('layouts.app')

@section('content')
    <chat-room :auth-user="{{ auth()->user() }}"></chat-room>
@endsection

Testing the Application

Now, let’s test what we’ve been building so far. First, let’s make sure our application is still running:

php artisan serve

Navigate to http://127.0.0.1:8000 and open the application in three separate browser windows and login and start chatting.

Conclusion

In this tutorial, we have explored how to make a functional chatroom using Laravel, Vue.js, and Stream Chat. The knowledge from here can be used to create more complex group chat and real-time applications.

Stream offers a real-time chat API and a wide variety of other features that can be useful in creating truly mature chat and in-app messaging solutions. You can learn more about Stream Chat here.

The complete code for this tutorial is available on GitHub.

Integrating Video With Your App?
We've built a Video and Audio solution just for you. Check out our APIs and SDKs.
Learn more ->