Build a Chat Room With Node.js and Socket.io
By: Usman Saleem
Tags: Nodejs, Chat, Room, Socketio, React

Introduction

Socket.IO is a library that enables real-time, duplex communication between client and the server.

It contains Websocket which is a protocol that provides a full-duplex and low-latency channel between the server and the browser

Implementation in Node.js:

Basic overview:

const socketio = require(“socket.io”) // To get the package

const io = socketio(server) // here sever is http-server

io.on(‘connect’, ()=>{}) // this is first function called when connection is made between client and server

socket.on(‘join’, ()=> {}) and socket.join(‘user room’) //these events for joining the room

socket.emit(‘name of event’, ()=>{}) // it emits the event which both client or server will listen to

socket.on(‘name of event’,()=>{}) this is used both in client side and server side to listen to the event which is emitted by socket.emit and run the code inside it

socket.on(‘disconnect’,()=>{}) // it will tell when the user has disconnected

Server code integration (node):

create and cd SampleApp npm init // to setup npm package npm i --save express // i am using express.js npm i --save socket.io // for socket.io

After everything is installed. Create a file name server.js and started writing the code

server.js

const http = require('http');

const express = require('express');

const socketio = require('socket.io');

const app = express(); const server = http.createServer(app);

const io = socketio(server); const {

addUser,

removeUser,

getUser,

getUsersInRoom

} = require('./utils/User'); io.on('connect', socket => {

socket.on('join', ({ username, room }, callback) => {

const { error, user } = addUser({ id: socket.id, name: username, room }); // add user with socket id and room info



if (error) return callback(error);



socket.join(user.room);



socket.emit('message', {

user: 'adminX',

text: `${user.name.toUpperCase()}, Welcome to ${user.room} room.`

});

socket.broadcast.to(user.room).emit('message', {

user: 'adminX',

text: `${user.name.toUpperCase()} has joined!`

});



io.to(user.room).emit('roomData', {

room: user.room,

users: getUsersInRoom(user.room) // get user data based on user's room

});



callback();

}); socket.on('sendMessage', (message, callback) => {

const user = getUser(socket.id);



io.to(user.room).emit('message', { user: user.name, text: message });



callback();

}); socket.on('disconnect', () => {

const user = removeUser(socket.id);



if (user) {

io.to(user.room).emit('message', {

user: 'adminX',

text: `${user.name.toUpperCase()} has left.`

});

io.to(user.room).emit('roomData', {

room: user.room,

users: getUsersInRoom(user.room)

});

}

}); server.listen(process.env.PORT || 3000, () =>

console.log('Server is running')

);

after establishing the connection and user joins the room. There will be a socket.emit event which will be listened by socket.on

socket.on(‘sendMessage’) event will be listened which contains user name and message. After this connection will be disconnected when user leaves the chat and an event will be emitted.

Create a user.js file for user crud

users.js

//Array of users

const users = [];



const addUser = ({ id, name, room }) => {

name = name.trim().toLowerCase();

room = room.trim().toLowerCase();



const existingUser = users.find(

user => user.room === room && user.name === name

);



if (!name || !room) return { error: 'Username and room are required.' };

if (existingUser) return { error: 'Username already exists.' };



const user = { id, name, room };



users.push(user);



return { user };

};



const removeUser = id => {

const index = users.findIndex(user => user.id === id);



if (index !== -1) return users.splice(index, 1)[0];

};



const getUser = id => users.find(user => user.id === id);



const getUsersInRoom = room => users.filter(user => user.room === room);



module.exports = { addUser, removeUser, getUser, getUsersInRoom };

Same events will be listened at client as well. I am using React for front-end.

Client code integration (React):

create and cd ClientSample npm i --save socket.io-client // for socket.io client

After installation. Create a file name clientSocket.js and add code like

import { useState, useEffect, useRef } from 'react';

import socketIOClient from 'socket.io-client';



const ENDPOINT = 'http://localhost:3000/';



const ClientSocket = ({ username, room }) => {

const socketRef = useRef();

const [users, setUsers] = useState([]);

const [messages, setMessages] = useState([]);

const [error, setError] = useState('');



useEffect(() => {

socketRef.current = socketIOClient(ENDPOINT);



socketRef.current.emit('join', { username, room }, error => {

if (error) {

setError(error);

}

});



return () => {

socketRef.current.emit('disconnect');

};

}, [username, room]);



useEffect(() => {

socketRef.current.on('message', message => {

setMessages(messages => [...messages, message]);

});



socketRef.current.on('roomData', ({ users }) => {

setUsers(users);

});

}, []);



const sendMessage = message => {

socketRef.current.emit('sendMessage', message, () => {});

};



return { users, messages, sendMessage, error };

};



export default ClientSocket;

It will use same events which is used by server to emit or listen every time user gets a message. After this, create Chat.js file

const Chat = () => {

const history = useHistory();

const { username, room } = useParams(); //these are added when user registers const [message, setMessage] = useState(''); const { users, messages, sendMessage} = ClientSocket({

username,

room

}); const onSubmitHandler = event => {

event.preventDefault();

if (message) sendMessage(message);

setMessage('');

}; const renderActiveUsers = () => {

return users.map(user => {

return (

<p key={user.id} >

<i className="fa fa-circle"></i> {user.name}

</p>

);

});

}; return ( <div className="container-fluid d-flex">

<div className="col-lg-6">

<ul>

<h6>Messages</h6>

{messages.text}

</ul>

<form onSubmit={onSubmitHandler}>

<input

autoComplete="off"

value={message}

onChange={e => setMessage(e.target.value)}

type="text"

name="message"

/> <button type="submit"> Send </button

</form>

</div> <div className="col-lg-6 border-left ml-2">

{renderActiveUsers()}

</div>

</div> )};

export default Chat;

Conclusion

Above code is simple implementation of how we setup socket.IO in both client and server. It’s an awesome and easy to integrate library.

I wrote very minimal code to let you know about the socket.io. If you want to improve it you can:

Write separate react(jsx) files to give it a chat app feel and apply styling.

Add online/offline labels

Thank you for reading .Hope you like it. :)

Reference