Have you ever wondered how real-time apps like chat apps and online games are built? If you’ve never built one before, read this tutorial as I’m going to show you how to build a simple one using socket.io.
What are we going to build?
It’s a simple app with one button and a label below it. The label displays “Likes: X” (where x is the current number of likes). When the user clicks on the button, the number of likes increases by one.
We’re going to make it real time by showing users on the app how the number of likes increases as other users are clicking on the button. So you don’t need to reload the page to see the latest value.
Here’s how the app would look like:
You can get the source code of this project on GitHub.
Creating a new project
In a new folder, add package.json using npm init -y
, and then install these three packages:
npm install express ejs socket.io
We’ll use ejs as the templating engine, and socket.io for making our app a real-time app.
Displaying a hello world page
As mentioned above, we’ll use ejs for rendering our views. So create index.ejs and add the following:
Realtime like app
Hello World!
Now let’s create our node server and serve the above file as the homepage.
So create node.js and add this:
const app = require('express')()
const path = require('path')
app.engine('html', require('ejs').renderFile)
app.set('view engine', 'html')
app.get('/', (req, res) => {
res.render(path.join(__dirname '/index.ejs'), null, (err, html) => {
res.send(html)
})
})
app.listen(3000, () => console.log('the app is running on localhost:3000'))
So we created a new server that runs on port 3000
. When the user hits http://localhost:3000/ in the browser, we’ll render index.ejs and display it.
If you run the app using node index.js
(or using nodemon if you want the app to restart automatically on changes) and open http://localhost:3000/, you should see “Hello World!” displayed.
Adding style.css
This isn’t a CSS tutorial, so let’s quickly add style.css in the root directory and fill it with this:
body {
background: hsl(0, 50%, 80%);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
padding: 0;
}
button {
background: hsl(0, 50%, 90%);
border: none;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
width: 150px;
height: 150px;
cursor: pointer;
outline: none;
box-shadow: 0 14px 28px hsla(0, 50%, 10%, 25%), 0 10px 10px hsla(0, 50%, 10%, 22%);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}
button:hover {
box-shadow: 0 1px 3px hsla(0, 50%, 10%, 12%), 0 1px 2px hsla(0, 50%, 10%, 24%);
}
button:active {
box-shadow: none;
}
svg path {
fill: hsl(0, 30%, 30%);
}
.main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.likes {
margin-top: 20px;
color: hsl(0, 60%, 20%);
font-weight: bold;
font-family: sans-serif;
text-transform: uppercase;
font-size: 20px;
}
Now let’s tell our server about it so when we request it, it responds with this file.
Add this route in index.js (below the root route):
app.get('/style.css', (req, res) => {
res.sendFile(path.join(__dirname '/style.css'))
})
And then let’s use it in our index.ejs by adding this at the bottom of :
Displaying the button and the label
Open index.ejs and update it like this:
Realtime like app
Likes: <%= likes %>
For this to work, we have to pass likes
from the server when rendering the template.
So open index.js and update the root route like this:
let likes = 0
app.get('/', (req, res) => {
res.render(path.join(__dirname '/index.ejs'), { likes }, (err, html) => {
res.send(html)
})
})
Note how we defined likes above it.
To keep this example simple, we defined likes in the memory, which means its value will go back to 0 when the server restarts. Typically in real-world apps you’ll have your data stored in the database.
Incrementing likes by clicking on the button
To do so, we need to add a new route that increments likes and returns the new value. And then we’ll make a request to this endpoint from index.ejs, when the user clicks on the button.
Let’s define the route first, in index.js.
app.post('/like', (req, res) => {
likes
res.json({ likes })
})
So it’s a POST endpoint at /like
.
Now let’s listen for the button’s click event and send this request using the Fetch API.
Add the following above