Post

Node.js Socket.io Chat app

prerequisite : node.js, javascript

Select a starting folder for the project

execute npm init -y on terminal

1
2
├── CHAT_TEST
│   └── package.json

install dependencies

npm install –save express nodemon socket.io

npm install –save babel-cli babel-preset-env babel-preset-stage-0

starting setting in package.json

1
2
"scripts": {
    "start": "nodemon ./index.js --exec babel-node -e js",

make .babelrc file in the root of the project.

1
2
3
4
5
6
{
    "presets": [
        "env",
        "stage-0"
    ]
}

make index.js, index.html

1
2
3
4
5
├── CHAT_TEST
│   ├── public
│   │   └── index.html
│   ├── package.json
│   └── index.js

index.html (client)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chatter</title>
</head>
<body>  
    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io.connect('http://localhost:3000');
        socket.on('message', (data) => {
		        // listen to message
            console.log(data);
            // send message to client
            socket.emit('another event', { jeremy: 'I am great thank you' });
        });
    </script>
</body>
</html>

index.js (server)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const app = require('express')(); // execute express right away
const server = require('http').Server(app) // http server
const io = require('socket.io')(server);
const port = 3000;

server.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});
// ------------------------------------ run server

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/public/index.html');
})

io.on('connection', (socket) => {
    console.log('user connected');
    // send a message to server
    socket.emit('message', { manny: 'hey how are you?' });

    // wait an event
    socket.on('another event', (data) => {
        console.log(data);
    })
});

server(index.js) io.on('onnection’, to user → socket.emit('message', { user1: 'hello'}); send a message → socket.on('another event', (data) => {console.log(data); })

client(index.html) const socket = io.connect('http://localhost:3000'); to connect to server → socket.on('message', (data) => { to listen to a message from server → socket.emit('another event', {user2: 'hi'});

Include bootstrap from getbootstrap.com

1
2
3
4
5
6
7
8
9
10
11
<head>
...
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
        crossorigin="anonymous"></script>
        
...   

Add Jquery

1
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

add style and form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="container-fluid">
    <div class="row">
        <ul id="messages"></ul>
    </div>
    <div class="row">
        <div class="col-lg-6">
            <form action="">
                <div class="input-group mb-3">
                    <input id="m" autocomplete="off" type="text" class="form-control" placeholder="Message..."
                        aria-label="Message...">
                    <button class="btn btn-outline-secondary" type="submit">Send</button>
                </div>
            </form>
        </div>
    </div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<style>
        form {
            padding: 20px;
            position: fixed;
            bottom: 0;
            width: 100%;
            padding-right: 50px;
        }

        #messages {
            list-style-type: none;
            margin: 0;
            padding: 0;
            width: 100%;
        }

        #messages li {
            padding: 5px 10px;
        }

        #messages li:nth-child(odd) {
            background: #eee;
        }
    </style>

Test interactions

modify html page

1
2
3
4
5
6
7
<script>
    const socket = io();
    $('form').submit(() => {
        socket.emit('message', $('#m').val());
        $('#m').val('');
        return false;
    })

This means socket emits whatever gets from <input id="m" and clear the value of the element.

1
2
3
4
    socket.on('message', (msg) => {
        $('#messages').append($('<li>').text(msg));
    });
</script>

Then it listen to a new message from server and put the message on <ul id="messages"></ul>

Change the server code

1
2
3
4
5
6
7
io.on('connection', (socket) => {
    console.log('user connected');
    socket.on('message', (msg) => {
        console.log('message', msg);
        io.emit('message', msg);
    })
});

Server listen to message from client and emit them.

Connect and disconnect event

connect : index.html

1
2
3
4
socket.on('connect', () => {
    // emiting to everybody
    socket.emit('message', 'user connected');
})

disconnect : index.js

1
2
3
4
5
6
7
8
io.on('connection', (socket) => {
...

    socket.on('disconnect', () => {
        console.log('user disconnected');
        io.emit('message', 'user disconnected');
    })
})

server listen to the disconnected event from client.

Namespace

server.js

1
2
3
4
5
6
7
8
9
10
11
const tech = io.of('/tech');

tech.on('connection', (socket) => {
    socket.on('message', (msg) => {
        tech.emit('message', msg);
    });

    socket.on('disconnect', () => {
        tech.emit('message', 'user disconnected');
    })
})

index.html

1
2
<script>
    const socket = io('/tech');

Room, setup

make room feature on the client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// room name
const room = 'javascript';

$('form').submit(() => {
    let msg = $('#m').val();
    socket.emit('message', { msg, room }); // set the room when send message
    $('#m').val('');
    return false;
});

socket.on('connect', () => {
    // emiting to everybody
    socket.emit('join', { room: room }); // take the join event with its room name
})

make room feature on the server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tech.on('connection', (socket) => {
	// join by room
  socket.on('join', (data) => { // take an object { msg, room }
      socket.join(data.room);
      tech.in(data.room).emit('message', `New user joined ${data.room} room!`); 
      // io.in(room).emit('message', 'msg');
  });

  // send message by room
  socket.on('message', (data) => { // take an object { msg, room }
      socket.join(`message: ${data.msg}`);
      tech.in(data.room).emit('message', data.msg);
  });
  
  socket.on('disconnect', (data) => {
      console.log('user disconnected');
      tech.in(data.room).emit('message', 'user disconnected');
  })
})

Make a client page to enter each rooms (index.html, javascript.html)

  • change name of index.html to javascript.html to call from the menu
1
2
3
4
5
6
├── CHAT_TEST
│   ├── public
│   │   ├── javascript.html
│   │   └── index.html
│   ├── package.json
│   └── index.js
  • header
1
2
3
4
5
6
<div class="jumbotron jumbotron-fluid">
    <div class="container">
        <h1 class="display-3">Tech space chatter</h1>
        <p class="lead">Select which room you'd like to join below!</p>
    </div>
</div>
  • menu for rooms
1
2
3
4
5
6
7
8
9
<div class="container-fluid">
  <div class="row">
      <ul class="list-group">
          <a href="/javascript" class="list-group-item list-group-item-action">Javascript</a>
          <a href="#" class="list-group-item">Swift</a>
          <a href="#" class="list-group-item">CSS</a>
      </ul>
  </div>
</div>

Make routers for rooms (index.js)

1
2
3
app.get('/javascript', (req, res) => {
  res.sendFile(__dirname + '/public/javascript.html');
})

Make client htmls for multi rooms I(css, swift.html)

1
2
3
4
5
6
7
8
├── CHAT_TEST
│   ├── public
│   │   ├── javascript.html
│   │   ├── swift.html
│   │   ├── css.html
│   │   └── index.html
│   ├── package.json
│   └── index.js
  • swift.html
1
2
<script>
  const room = 'swift';
  • css.html
1
2
<script>
  const room = 'css';
  • index.html
1
2
3
4
5
<ul class="list-group">
    <a href="/javascript" class="list-group-item list-group-item-action">Javascript</a>
    <a href="/swift" class="list-group-item">Swift</a>
    <a href="/css" class="list-group-item">CSS</a>
</ul>

Add more routers for rooms (index.js)

1
2
3
4
5
6
7
8
9
10
11
app.get('/javascript', (req, res) => {
    res.sendFile(__dirname + '/public/javascript.html');
})

app.get('/css', (req, res) => {
    res.sendFile(__dirname + '/public/css.html');
})

app.get('/swift', (req, res) => {
    res.sendFile(__dirname + '/public/swift.html');
})

Debugging

you can add start script on package.json

Socket.io cheatsheet

https://socket.io/docs/v3/emit-cheatsheet/

This post is licensed under CC BY 4.0 by the author.