Building simple chat app with Socket.IO

socketio eyecatch

A lot of websites today have a realtime chat function. Building your own chat library from scratch is difficult and time consuming. Today I will bring you a framework that is not only used for handling chat functions but also many other applications.

Features

Real-time communication: Enables instant, real-time messaging between clients and servers.
HTTP long-polling fallback: The connection will fall back to HTTP long-polling when it fails to establish websocket connection.
Cross-Browser compatibility: Works across all major browsers and even in environments where WebSockets are not supported.
Automatic reconnection: Attempts to reconnect automatically when the connection is lost.
Multiplexing: Supports multiple independent channels of communication over a single connection.
Namespaces: Allows you to create different communication channels within the same connection.
Rooms: Enables broadcasting messages to all clients in a specific room.
Many programming language implementations: Java, C++, Python, swift, etc.

Applications

Chat applications: Real-time messaging between users.
Online gaming: Real-time interactions between players.
Collaboration tools: Real-time updates in collaborative environments (e.g., document editing).
Live feeds: Real-time data streams for news, sports, or financial updates.
Notification systems: Real-time notifications for various events (e.g., social media notifications).

How it works

Socket.IO is NOT a websocket implementation. It uses websocket as transport to transfer data when possible.

Transports

HTTP long-polling: Use normal HTTP requests to transfer data. Using GET requests for receiving data from the server. POST requests for sending data to the server.
Websocket(WS): This connection provides a bidirectional and low-latency communication channel between the server and the client.

Upgrade mechanism

Socket.IO doesn’t make a websocket connection right away. It makes a GET request first. If the server responds that it is okay to upgrade to websocket, then the connection will be upgraded to websocket. Otherwise using HTTP long-polling.

Event-based communication

With plain websocket connection we send raw data like string or binary data, then server and client will convert these data into usable format. Socket.IO on the other hand, will serialize/deserialize your data automatically. But you can always send raw data as you wish.
For example:

//client side
interface MyMessage {
  userId: number;
  content: string;
};
const message: MyMessage = {
  userId: 10,
  content: "Hello"
};
socket.emit("new-message", message);
//server side
io.on("new-message", (data: MyMessage) => {
  console.log(data.userId);
});  

As you can see, the server doesn’t need extra steps to transform data from the client. This makes development more convenient.

Install

Socket.IO library can be installed by using node.js package managers like npm or yarn . Frontend(FE) will use the client library(socket.io-client) and Backend(BE) will use the server library(socket.io).

npm install -S socket.io
npm install -S socket.io-client

or

yarn add socket.io
yarn add socket.io-client  

Install bootstrap
This is optional. I want a better visualization so I use a library for UI.

npm install -S react-bootstrap bootstrap

or

yarn add react-bootstrap bootstrap

Then add CSS to your _app file.

import "bootstrap/dist/css/bootstrap.min.css";

Creating a chat application

I will create two folders, one for FE and one for BE.

socketio folder structure

Two new added files are index.js and chat.tsx. If you don’t have package.json you can create it manually or by using the command below.

npm init

or

yarn init

After creating project folders and files, you can copy the code below and paste it.

index.js

const server = require("http").createServer();
const io = require("socket.io")(server, {
  cors: {
    origin: "http://localhost:3000",//allow FE origin
    methods: ["GET", "POST"]
  }
});

const CHAT_MESSAGE_EVENT = "chat_message";

io.on("connection", (socket) => {
  socket.on(CHAT_MESSAGE_EVENT, (msg) => {
    //broadcast this message to all connected users
    io.emit(CHAT_MESSAGE_EVENT, msg);
  });
});

server.listen(3001, () => {
  console.log("server running at http://localhost:3001");
});

chat.tsx

import Head from "next/head";
import { useEffect, useState } from "react";
import { Button, Col, Container, Row } from "react-bootstrap";
import Form from "react-bootstrap/Form";
import { io } from 'socket.io-client';

const socket = io("http://localhost:3001", {
  autoConnect: false
});

const CHAT_MESSAGE_EVENT = "chat_message";

export default function Chat() {
  const [message, setMessage] = useState("");
  const [userName, setUserName] = useState("user01");
  const [messageList, setMessageList] = useState<string[]>([]);

  const onSendMessage = () => {
    if (!socket.connected || !message || !userName) {
      return;
    }
    socket.emit(CHAT_MESSAGE_EVENT, `${userName}:${message}`);
    setMessage("");
  };

  useEffect(() => {
    socket.connect();

    const onChatMessage = (data: string) => {
      setMessageList((prev) => [...prev, data]);
    };

    socket.on(CHAT_MESSAGE_EVENT, onChatMessage);

    return () => {
      socket.off(CHAT_MESSAGE_EVENT, onChatMessage);
    };
  }, []);

  return (
    <>
      <Head>
        <title>Socket.IO Chat</title>
        <meta name={"description"} content={"chat page"} />
        <meta name={"viewport"} content={"width=device-width, initial-scale=1"} />
        <link rel={"icon"} href={"/favicon.ico"} />
      </Head>
      <Container className={"vh-100 overflow-hidden d-flex flex-column justify-content-between"}>
        <Container className={"flex-grow-1 overflow-auto pt-2"} fluid>
          {messageList.map((v, i) => (
            <Row key={i} xs={12} style={{ whiteSpace: "pre-line" }}>{v}</Row>
          ))}
        </Container>
        <Row className={"py-2"}>
          <Col xs={12} md={8}>
            <Form.Control value={message} onChange={(e) => setMessage(e.target.value)} as={"textarea"} rows={4} placeholder={"message"} />
          </Col>
          <Col xs={12} md={4}>
            <Form.Label>User name:</Form.Label>
            <Form.Control value={userName} onChange={(e) => setUserName(e.target.value)} placeholder={"user name"} />
            <Button className={"mt-2"} onClick={onSendMessage}>Send</Button>
          </Col>
        </Row>
      </Container>
    </>
  );
}

Explanation

FE: We have two inputs for inputting messages, user name and message list. user name and message will be sent to BE by clicking the send button. To start FE, open your terminal in the FE folder and run the command below.

npm run dev

or

yarn run dev

Your chat page url will be http://localhost:3000/chat
BE: It will broadcast your message to all connected users. To start BE, open your terminal in the BE folder and run the command below.

node index.js

Result

socketio chat demo

I opened two windows to test. As you can see, the user name is included in the message so we can know who sent it.

Summary

Socket.IO is a good framework for realtime communication. It supports many programming languages and is easy to integrate into your application. So let’s give it a try!
References:
Socket.IO
Next.js by Vercel – The React Framework (nextjs.org)
React Bootstrap | React Bootstrap (react-bootstrap.netlify.app)

関連記事

カテゴリー:

ブログ

情シス求人

  1. チームメンバーで作字やってみた#1

ページ上部へ戻る