Build a chat app for a live event with Node.js, MySQL and Pusher Channels Part 1: Building the login page
A basic understanding of Node.js and JavaScript (ES6 syntax) would help you with this tutorial. However, it is not a requirement as this is a step by-step guide. You will need to have Node.js, NPM, and MySQL installed on your machine.
In this two-part tutorial we’ll build a simple chat room that can be featured alongside a live event. We’ll take advantage of the user concept in Pusher to build strong authentication and moderation features, and offer your users a safer and more enjoyable chat experience.
The backend is built using Node.js and Express while we’ve used JavaScript for the frontend and Pusher Channels.
Pusher user concept
The Pusher User classifies users as a distinct concept in the Pusher system. It helps automate user-driven development, for example, to identify connected users and address them individually or terminate connections based on malicious behavior.
Requirements
Before you can start implementing this tutorial, you need the following:
- An IDE of your choice, e.g. Visual Studio Code.
- NMP
- Node.js (version >= 10.x) installed on your computer. Download Node.js
- MySQL database
Implementation steps
Here is the step-to-step implementation guide.
Part 1 of this tutorial covers steps 1 to 5 - from how to set up Pusher Channels, enable client events, get App Keys, set the code base, and build the login page.
Part 2 continues with steps 6 to 8 on how to build and test the homepage with a chat widget, build the admin dashboard, run the app and see it all in action.
Step 1: Set up Pusher Channels
This section explains how to set up Pusher Channels.
Create an app
- Navigate to pusher.com and sign up for a free account. If you already have an account, sign in.
NOTE: Pusher’s free sandbox plan comes with 200,000 messages per day and 100 concurrent connections (total number of simultaneous connections across your app) so it will be more than sufficient for this tutorial app.
-
Once signed in, navigate to Channels and click Create app.
-
Configure your app by proving the following information:
- App name - A name is for your reference and you can change it later.
- Cluster - The physical location of the servers that handle requests from your Channels app. It’s not possible to change the cluster afterward.
- Tech stack you intend to integrate Pusher with for a better setup experience (optional).

- Click Create app to continue.
Step 2: Enable client events
For Pusher to trigger events from the client (browser), you need to use Presence channels. It will enable chat participants to securely chat with one another without having to send messages through the backend server first.
Here’s how to enable client events:
Navigate to your channel app > App Settings > toggle the Enable client events option.

Step 3: Get App Keys
To use Pusher channels, you need App Keys for your app.
Go to your app, from the side navigation, click App Keys.
Step 4: Set up the codebase
To create a new application, create a new directory:
mkdir pusher-event-chat
Now, go ahead and change the directory into the newly created folder:
cd pusher-event-chat
Check dependencies
To go through the tutorial seamlessly, install the following dependencies:
- Pusher
NodeJS SDK dotenvfor parsing the variable.env fileexpressto configure and manage an HTTP serverexpress-sessionfor providing an Express middlewarebody-parserfor providing body parsing middlewaremysqlfor connecting with the databasejs-sha512for hashing user passwordsjsdomfor emulating a subset of a web browser
Get those dependencies by running:
npm install pusher dotenv express express-session body-parser mysql js-sha512 jsdom -- save
Create project subdirectories
In the pusher-event-chat directory, create directories called server and public.
mkdir server
mkdir public
Create nested directories:
mkdir public/admin
mkdir public/landing
mkdir public/login
Setup MySQL database
NOTE: Make sure you have the MySQL server installed and running on your machine. You can use the following command to check this:
mysqladmin -u root -p ping
Because we’re trying to build the webpage to manage the event, we need a mechanism to store users’ data to determine which users can be part of the event. For this, we’ll need a database to connect to.
Let’s start by creating the event database and the table of accounts for tickets with an association with the users who have registered them.
Execute the following SQL statement either with the preferred MySQL Editor or with the command line. The tutorial follows the CLI approach.
- Enter the
serverdirectory by
cd server
- Create the
eventdb.sqlfile by running:
touch eventdb.sql
- Paste in the following contents to the newly created file:
/* pusher-event-chat/server/eventdb.sql */
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';
FLUSH PRIVILEGES;
CREATE DATABASE IF NOT EXISTS `eventdb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `eventdb`;
CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`ticket` varchar(255) NOT NULL,
`email` varchar(100) NOT NULL,
`fullname` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `accounts` (`id`, `username`, `ticket`, `email`, `fullname`) VALUES (1, 'admin', 'c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec', 'admin@demo.com', 'admin'); /* admin */
INSERT INTO `accounts` (`id`, `username`, `ticket`, `email`, `fullname`) VALUES (2, 'john', 'b7fcc6e612145267d2ffea04be754a34128c1ed8133a09bfbbabd6afe6327688aa71d47343dd36e719f35f30fa79aec540e91b81c214fddfe0bedd53370df46d', 'john@demo.com', 'John Smith'); /* john */
INSERT INTO `accounts` (`id`, `username`, `ticket`, `email`, `fullname`) VALUES (3, 'mike', 'a91d24d7eab7683bc73b857d42dfc48a9577c600ccb5e7d7adabab54eebc112232f3de2539208f22a560ad320d1f2cda5a5f1a127baf6bf871b0e282c2b85220', 'mike@demo.com', 'Mike Wilson'); /* mike */
The above SQL statement will create the database eventdb and create the accounts table. In addition, it will insert test accounts that we can use for demo purposes.
- Use the following command to connect to your MySQL database server:
mysql -uproot -p
In this tutorial, we’re using the default MySQL server configuration and connect as a root user with no password.
NOTE: This is done for demonstration purposes only and must be changed for the production environment.
- Execute queries from the
eventdb.sqlby running:
mysql> \. /full/path/to/the/pusher-event-chat/server/eventdb.sql
- Verify if data is available in the
accountstable by executing:
mysql> select * from eventdb.accounts;
- The result of this query should be:

- Exit MySQL client:
mysql> quit
Application credentials
Now you need to save Pusher App credentials.
- Create the .env file in the
./serverdirectory:
touch variable.env
- Paste in the following contents to the newly created file, and update values with your app credentials obtained from the dashboard. Refer back to Step 3 - Get App Keys.
// pusher-event-chat/server/variable.env
PUSHER_APP_ID="YOUR_PUSHER_APP_ID"
PUSHER_APP_KEY="YOUR_PUSHER_APP_KEY"
PUSHER_APP_SECRET="YOUR_PUSHER_APP_SECRET"
PUSHER_APP_CLUSTER="YOUR_PUSHER_APP_CLUSTER"
Setup Express and Pusher
Let’s start fleshing the server.
- Run command to create a new file
server.jsin theserverdirectory:
touch server.js
- Open the newly created file and paste the code below into it:
// pusher-event-chat/server/server.js
require('dotenv').config({
path: 'variable.env'
});
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const session = require('express-session');
const Pusher = require('pusher');
const mysql = require('mysql');
const sha512 = require('js-sha512').sha512;
var jsdom = require("jsdom");
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_APP_KEY,
secret: process.env.PUSHER_APP_SECRET,
cluster: process.env.PUSHER_APP_CLUSTER,
forceTLS: true,
});
Here, we create a Pusher instance. As you can see, we’re loading our Pusher app credentials from environment variables in the variable.env file we created earlier.
- Create a basic Express server and enable some middleware that the server will use. The session middleware will allow us to save a user’s details in session for later use. The body-parser middleware will allow us to get form data. Then we serve static files from the public directory.
const app = express();
app.use(session({
secret: 'somesecrethere',
resave: true,
saveUninitialized: true
}))
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(express.static(path.join(__dirname, '/../public')));
- Start the server on port 3000.
app.listen(3000, () => {
console.log('Server is up on 3000')
});
Step 5: Build the login page
In this section, we will be operating on two files. This is server.js and login.html. We additionally will create login.css to make the login form more appealing.
Creating the CSS for the Login Form
- Navigate to
public/logindirectory and createlogin.cssfile:
cd ../public/login
touch login.css
- Edit the
login.cssfile and add:
/* pusher-event-chat/public/login/login.css */
* {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "segoe ui", roboto, oxygen, ubuntu, cantarell, "fira sans", "droid sans", "helvetica neue", Arial, sans-serif;
font-size: 16px;
}
body {
background-color: #435165;
}
.login {
width: 400px;
background-color: #ffffff;
box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.3);
margin: 100px auto;
}
.login h1 {
text-align: center;
color: #5b6574;
font-size: 24px;
padding: 20px 0 20px 0;
border-bottom: 1px solid #dee0e4;
}
.login form {
display: flex;
flex-wrap: wrap;
justify-content: center;
padding-top: 20px;
}
.login form label {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
background-color: #3274d6;
color: #ffffff;
}
.login form input[type="password"], .login form input[type="email"] {
width: 310px;
height: 50px;
border: 1px solid #dee0e4;
margin-bottom: 20px;
padding: 0 15px;
}
.login form input[type="submit"] {
width: 100%;
padding: 15px;
margin-top: 20px;
background-color: #3274d6;
border: 0;
cursor: pointer;
font-weight: bold;
color: #ffffff;
transition: background-color 0.2s;
}
.login form input[type="submit"]:hover {
background-color: #2868c7;
transition: background-color 0.2s;
}
The stylesheet file consists of properties that are associated with HTML elements.
Create login template with HTML
The login form consists of an HTML form element and input elements. This enables the user to enter their details and submit.
- Create
login.htmlfile in the/public/logindirectory:
touch login.html
- And edit by adding the code:
<!-- pusher-event-chat/public/login/login.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1">
<title>Login</title>
<!-- the form awesome library is used to add icons to our form -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" >
<link href="./login/login.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="login">
<h1>Login</h1>
<form action="/login" method="post">
<label for="email">
<i class="fas fa-user"></i>
</label>
<input type="email" name="email" placeholder="email" id="email" required>
<label for="password">
<i class="fas fa-lock"></i>
</label>
<input type="password" name="ticket" placeholder="your ticket number" id="ticket" required>
<input type="submit" value="Login">
</form>
</div>
</body>
</html>
The template enables users to submit their details. We’ll be using a POST request to capture the data, which we can then handle in our Node.js login route.
Add login logic with Node.js
Now that we have all our login frontend finished, we can start developing the server-side with Node.js.
- Go back to the
./server/server.jsfile and add the initial route which will render a login template:
app.get('/', function(request, response) {
if (request.session.loggedin) {
if (request.session.isadmin) {
return response.sendFile(path.join(__dirname + '/../public/admin/admin.html'));
} else {
return response.sendFile(path.join(__dirname + '/../public/landing/index.html'));
}
} else {
response.sendFile(path.join(__dirname + '/../public/login/login.html'));
}
});
At this point, our code has pretty straightforward logic. After a new client’s connection to the server is established, the login.html file is displayed. If the user is already logged in, they will have a different view.
- Now we need to add user authenticatication to our site. Authenticatication, in this case, requires verification if the user has registered the ticket. If the data the user provided is correct, we will create a browser session. Therefore, we need to create a database connection first. Continue editing the
server.jsfile by pasting in the following code:
const connection = mysql.createConnection({
host : '127.0.0.1',
port : '3306',
user : 'root',
password : '',
database : 'eventdb'
});
- Add a new route that will authenticate the user:
app.post('/login', function(request, response) {
let email = request.body.email;
let ticket = request.body.ticket;
if (email && ticket) {
connection.query('SELECT * FROM accounts WHERE email = ? AND ticket = ?', [email, sha512(ticket)], function(error, result, fields) {
if (error) throw error;
if (result.length > 0) {
request.session.loggedin = true;
request.session.email = result[0].email;
request.session.username = result[0].username;
request.session.fullname = result[0].fullname;
if (request.session.username === 'admin') {
request.session.isadmin = true
}
return response.redirect('/');
} else {
return response.send('Incorrect input data provided!');
}
});
} else {
return response.send('Please enter username, email and ticket number!');
}
});
Let’s narrow down what the code will do. The above code creates the authentication route using the POST method, which will capture input fields when the user submits the login form. The code verifies whether the email and ticket fields are filled in.
NOTE: This tutorial uses a ticket number as a password. This is done for demo purposes only and must be changed for the production environment. However, even for quick demo purposes, we don’t store passwords as plain text in the database. Safety first!
After the input verification code selects the desired account from our MySQL database.
The user is successfully authenticated and redirected to the landing page if the account exists. You may have noticed that we store some information about the user in the session. we’ll come back to that shortly, in part 2 of this tutorial.
Continue building your live chat room with Part 2: Add chat functionality and Admin dashboard.
9 June 2022
by Agata Walukiewicz