Create a Chat with Laravel and Socket.io
Published: 2024-05-07 14:40:29

Previously we created a chat with [pusher](https://dev.to/vimuth7/create-a-chat-with-laravel-and-pusher-4igp). but this time we are going to do it with [Socket.io](https://socket.io/). Socket.io is a NodeJS library. With it we can create our own servers. This is cheaper than using pusher server and we have more control on the code.

This is how to do it.

##**1. First we create a node server.**
Install NodeJS and run this command

```
npm init
```

And then run these command to install ioredis and sockets.io

```
npm i express ioredis socket.io
```

Now your package.json should look like this

```
{
  "name": "node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.19.2",
    "ioredis": "^5.4.1",
    "socket.io": "^4.7.5"
  }
}
```

Now create a file called index.js in root and add this code.

```
var server = require('http').Server();

var io = require('socket.io')(server, {
    cors: {
        origin: "*",  // Allows all origins
        methods: ["GET", "POST"]  // Allowed request methods
    }
});

const { json } = require('express');
var Redis = require('ioredis');
var redis = new Redis();

redis.subscribe('chats');

redis.on('message', function (channel, message) {
    message = JSON.parse(message);
    io.emit('chatsocket', message.data);
})


server.listen(3000);
```

Here it get's data from `chats` channel from redis and emit or send data via **chatsocket** channel for socket.io.

Now let's got to laravel application. We should setup all everything as previous pusher tutorial. But we have to do few things different.

in `resources\js\components\Chat.vue`

```
<template>
    <div v-if="!username">
        <input type="text" v-model="tempUsername" placeholder="Enter your username">
        <button @click="setUsername">Join Chat</button>
    </div>
    <div class="chat-box" v-else>
        <div class="chat-box-header">Chat</div>
        <div class="chat-box-messages" id="chat-messages">
            <div v-for="message in messages" :key="message.message_text" 
                :class="message.user == username ? 'message current-user' : 'message other-user'">
                {{ message.message_text }}
            </div>

        </div>
        <div class="chat-box-input">
            <input type="text" v-model="newMessage" class="input_border" placeholder="Type a message..." />
            <button type="button" @click="setMessage">Send</button>
        </div>
    </div>
</template>

<script setup>
    var socket = io('http://localhost:3000');

    import { onMounted, ref } from 'vue';

    const username = ref('');
    const tempUsername = ref('');
    const messages = ref([]);
    const newMessage = ref('');

    const setUsername = () => {
        username.value = tempUsername.value.trim();
        tempUsername.value = ''; 
    };

    const setMessage = () => {
        axios.post('/chat', {
            user: username.value,
            message_text: newMessage.value
        }).then(() => {
            let message = {
                user: username.value,
                message_text: newMessage.value
            }
            messages.value.push(message);
            newMessage.value = ""; 
        });
    }

     onMounted(async () => {
        
        socket.on('chatsocket', (data) => {
            let message = {
                user: data.chat.user,
                message_text: data.chat.message_text
            }
            
            messages.value.push(message);
        })
     });

</script>
```

Here we have used socket.io client and getting the event from Nodejs Server. And we send the message using the Redis. For this we need predis installed and configured in env.

And importantly you have to change `prefix => ''` in config\database.php,

```
 'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => '',
            // 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],
```