Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
8 views

JavaScript+Servers+-+Module+2+-+Noroff+Guide+-+PDF

asd

Uploaded by

stiastr95
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

JavaScript+Servers+-+Module+2+-+Noroff+Guide+-+PDF

asd

Uploaded by

stiastr95
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 164

JavaScript Servers - Module 2

Noroff Guide
Table of Contents

Module overview .............................................................................................. 4


Introduction ............................................................................................................................... 4
Learning outcomes ................................................................................................................ 4
2.1. Lesson - ExpressJS and HTML ........................................................... 6
Introduction ............................................................................................................................... 6
Learning outcomes ................................................................................................................ 7
Introduction to ExpressJS .................................................................................................. 7
Installation of ExpressJS ..................................................................................................... 9
Project overview ................................................................................................................... 12
Routing ...................................................................................................................................... 16
Middleware .............................................................................................................................. 22
Static HTML ............................................................................................................................. 27
Introduction to template engines .............................................................................. 33
Installation of EJS ................................................................................................................ 35
Templating - Layout ........................................................................................................... 39
Templating - Variables...................................................................................................... 48
What did I learn in this lesson? ................................................................................... 53
References ............................................................................................................................... 54
2.1. Lesson task - ExpressJS and HTML ............................................... 55
The task ..................................................................................................................................... 55
2.2. Lesson - Web sockets and ExpressJS application .................. 56
Introduction ............................................................................................................................ 56
Learning outcomes ............................................................................................................. 57
Introduction to web sockets ......................................................................................... 57
Installing Socket.IO ............................................................................................................. 59
Basic socket communication ......................................................................................... 63
Events ......................................................................................................................................... 67
Namespaces and rooms ................................................................................................... 70
Web sockets with template engine ........................................................................... 76
Adding rooms with request ........................................................................................... 78
Updating room list .............................................................................................................. 81
Saving rooms permanently ............................................................................................ 84
What did I learn in this lesson? ................................................................................... 88

JavaScript Servers - Module 2 Page i


References ............................................................................................................................... 88
2.2. Lesson task - Web sockets and ExpressJS application ....... 89
The task ..................................................................................................................................... 89
2.3. Lesson - Advanced Node Package Creation and
Authentication ................................................................................................. 90
Introduction ............................................................................................................................ 90
Learning outcomes ............................................................................................................. 91
Creating an NPM account ................................................................................................ 92
Package setup ........................................................................................................................ 94
Versions and tags ................................................................................................................ 99
Publishing package ..........................................................................................................101
Updating packages ...........................................................................................................105
Authentication and PassportJS ..................................................................................111
Project setup.........................................................................................................................112
Login prompt ........................................................................................................................116
Verify password..................................................................................................................119
Session .....................................................................................................................................122
What did I learn in this lesson? .................................................................................127
References .............................................................................................................................127
2.3. Lesson task - Advanced Node Package Creation and
Authentication .............................................................................................. 128
The task ...................................................................................................................................128
2.4. Lesson - Asynchronous NodeJS, streams and security .... 130
Introduction ..........................................................................................................................130
Learning outcomes ...........................................................................................................131
Callbacks and promises .................................................................................................131
Sequential and parallel execution ............................................................................136
Introduction to streams .................................................................................................139
Advanced streams .............................................................................................................142
Security overview...............................................................................................................148
Packages security ..............................................................................................................149
Data security ........................................................................................................................151
Server security .....................................................................................................................153
What did I learn in this lesson? .................................................................................155
References .............................................................................................................................155
2.4. Lesson task - Asynchronous NodeJS, streams and
security ............................................................................................................. 156

JavaScript Servers - Module 2 Page ii


The task ...................................................................................................................................156
2.5. Lesson - Self-study ............................................................................. 158
Introduction ..........................................................................................................................158
Materials .................................................................................................................................159
2.5. Lesson task - Self-study ................................................................... 161
The task ...................................................................................................................................161

JavaScript Servers - Module 2 Page iii


Module overview

Introduction
Welcome to Module 2 of JavaScript Servers.

If anything is unclear, check your progression plan and/or contact a tutor on


Discord.

Module structure Estimated workload

2.1. Lesson and task 8 hours


ExpressJS and HTML

2.2. Lesson and task 8 hours


Web sockets and ExpressJS application

2.3. Lesson and task 8 hours


Advanced Node Package Creation and
Authentication

2.4. Lesson and task 8 hours


Asynchronous NodeJS, streams and security

2.5. Lesson and task 8 hours


Self-study

Learning outcomes
In this lesson, we are covering the following knowledge learning
outcomes:
 The candidate has knowledge of concepts, processes, and tools used
in node-based JavaScript projects and solutions.

JavaScript Servers - Module 2 Page 4


 The candidate has knowledge of concepts, processes, and tools used
in Node Package Manager-based projects and solutions.
 The candidate has knowledge of concepts, processes, and tools used
in server-based JavaScript solutions.
 The candidate has knowledge of the industry of server-based
JavaScript solutions and is familiar with the field of work.
 The candidate can update their vocational knowledge of server-based
JavaScript solutions.

In this lesson, we are covering the following skill learning outcomes:


 The candidate can apply vocational knowledge of server-based
JavaScript solutions to practical and theoretical situations.
 The candidate masters using nodes to develop, deploy, and maintain
general-purpose JavaScript solutions.
 The candidate masters using Node Package Manager.
 The candidate masters using a server framework for building node-
based web servers.
 The candidate can find information and material relevant to selecting
appropriate node libraries for use in JavaScript solutions.

In this lesson, we are covering the following general competence


learning outcomes:
 The candidate understands the ethical principles that apply when
developing, deploying and maintaining server-based JavaScript
solutions.
 The candidate has developed an ethical attitude concerning the role
of JavaScript server developers and maintainers.
 The candidate can carry out work as a JavaScript server developer and
maintainer.

JavaScript Servers - Module 2 Page 5


2.1. Lesson - ExpressJS and HTML

Introduction

We have already learnt about the basics of NodeJS and NPM. Moreover, we learnt
how to set up a simple server using the NodeJS HTTP module and added simple
routing, which handles HTTP GET and POST requests.
However, to create a server, web frameworks are usually used instead. ExpressJS
is a very popular web framework hosted within a NodeJS environment. It provides
multiple tools to make features such as routing or setting application settings much
easier.
In this lesson, we’ll learn about the basics of ExpressJS – how to install it and use it
to create a simple server. Moreover, we’ll look at how it helps us with routing and
adding ‘middleware’ request processing.
After we have discussed the basics of ExpressJS – how to create simple
applications, implement routing and use middleware, we’ll learn how to display our
data to the users.

JavaScript Servers - Module 2 Page 6


We’ll explore how to link simple static HTML files to display them at proper
endpoints. Later, we’ll introduce variables and generate their values dynamically
with template engines.
There are several template engines we can use in NodeJS. While the default one is
Jade, we’ll learn how to use Embedded JavaScript (EJS). It is popular, easy to
understand, and makes creating websites with variables much easier.

Learning outcomes
In this lesson, we are covering the following knowledge learning
outcome:
 The candidate has knowledge of concepts, processes, and tools of
server-based JavaScript solutions.

In this lesson, we are covering the following skill learning outcomes:


 The candidate can apply vocational knowledge of server-based
JavaScript solutions to practical and theoretical solutions.
 The candidate masters the use of a server framework for building
Node-based web servers.

In this lesson, we are covering the following general competence


learning outcome:
 The candidate understands the ethical principles that apply when
developing, deploying and maintaining server-based JavaScript
solutions.

Introduction to ExpressJS
ExpressJS is one of the most popular web frameworks for NodeJS. It is used to
ease building websites, create web applications, and REST APIs with JavaScript.

JavaScript Servers - Module 2 Page 7


It supports numerous template engines – tools to use variables in our template
HTML files and replace them with actual values visible to the user after we run the
server. We’ll learn more about template engines in the next lesson.
With ExpressJS, we no longer have to handle requests by ourselves using the
NodeJS HTTP module. We can pass the request to ExpressJS using Middleware
functions (we’ll learn more about them in the next chapters) which will generate a
response for us. All we need to do then is to send the response back to the client.
This flow is presented in the picture below:

READ

1. Website: https://expressjs.com/
2. Page: 5.x API by ExpressJS.

WATCH

Video: What is Express? (1m 25s) by Jamie Pittman on LinkedIn


Learning.

JavaScript Servers - Module 2 Page 8


Installation of ExpressJS
While having NodeJS already installed, we can quickly get ExpressJS with the
NPM. To do this, we need to enter the working directory and use the command:
npm install express

We can also install the application generator, which will help us later with, for
example, creating a skeleton of the web application or adding template engines to
our project. To do this, let’s use the following command:
npm install express-generator -g

The -g flag will tell npm to install the package globally, so we’ll be able to use it in
all our future projects. After installation, we should ensure that our dependencies
were added correctly to our package.json file. It should look as follows:
{
"dependencies": {
"express": "^4.18.1",
}
}

Let’s create a very simple app with this generator. Let’s use the command:
express myapp

where “myapp” is the name of our application. There are many other options for
generating the application. We can see them after typing the command:
express -h

or checking an article on the official website (linked in the READ section). We


should get a result similar to the one on the screen below.

You might be shown a permission error that Express is not a command. This error
is encountered mainly in Windows 10 when VS Code terminal uses Power Shell as

JavaScript Servers - Module 2 Page 9


a default command line option. To fix this, type the following command in the
console:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope
CurrentUser

This line gives the current user the necessary permissions.

Now we can run the application with commands:


Enter the created myapp folder:
cd myapp

Install dependencies defined in the package.json file:


npm install

JavaScript Servers - Module 2 Page 10


Run the application:
npm start

If everything was done correctly, open the link http://localhost:3000/in the browser.
You should get the following result:

This is the home page of our Express application. It is a default template for each
application generated with the express-generator package. It is displayed because
http://localhost:3000/ is the home address of our local application.

JavaScript Servers - Module 2 Page 11


READ

1. Page Installing by ExpressJS.


2. Page: Express application generator by ExpressJS.

WATCH

Video: What is Express Application Generator? (8m 57s) by


Jamie Pittman on LinkedIn Learning.

Project overview
Let’s check the generated structure of our project:

JavaScript Servers - Module 2 Page 12


The bin folder contains the “www” JavaScript file. In there, we create the server and
set its port, for example. Port is set in the 15th line:
var port = normalizePort(process.env.PORT || '3000');

Variable process.env.PORT isn’t set yet (we’ll learn about this in the next modules),
so we need to provide port as the string (current value – ‘3000’)
We already know that the node_modules folder contains files from our
dependencies, so we won’t edit anything there.
We’ll place our resources (such as images), JavaScript programs, or CSS
stylesheets in a public folder.
The routes folder contains files responsible for our request. By default, we can
handle two GET requests to http://localhost:3000/ and http://localhost:3000/users
We already saw the result of the first one, so let’s check the result of the second by
opening the link in a browser while the application is running:

JavaScript Servers - Module 2 Page 13


And see the routes/users.js code responsible for this:
var express = require('express');
var router = express.Router();

/* GET users listing. */


router.get('/', function(req, res, next) {
res.send('respond with a resource');
});

module.exports = router;

In Express web apps, each endpoint is handled in a separate JavaScript file, unlike
in solution, with only a standard NodeJS HTTP module. This way, we can easily
implement new endpoints while keeping the application structure clean.
The views folder contains our template engine files, generating HTML files to
display to the user. By default, Jade is used as the template engine. We won’t edit
these files in this lesson.
The “app.js” file creates the instance of the Express application with the command:
var app = express();

and exports it as the “app” module. We can use all Express functionalities just by
requiring this module.

JavaScript Servers - Module 2 Page 14


READ

Page: Serving static files in Express by ExpressJS.

WATCH

1. Video: Getting started: Server and project setup (16m


24s) by Jamie Pittman on LinkedIn Learning.
2. Video: Serving static files with Express (6m 36s) by
Jamie Pittman on LinkedIn Learning.

JavaScript Servers - Module 2 Page 15


ACTIVITY

1. Change the port used by the application to 8080.

Answer: Click here to reveal /bin/www.js, 15th line.


2. By default, the font size inside the body is 14px.
Change it to 30px. You can check the result by visiting
the main page http://localhost:3000/ (or
http://localhost:8080/ if you changed the port).

Answer: Click here to reveal


/public/stylesheets/style.css.
3. Change the response from /users endpoint from
“respond with a resource” to “you should see a list of
the users.”

Answer: Click here to reveal /routes/users.js.

After finishing these exercises, revert (undo) them.

Routing
To handle an HTTP request with Express, all we need to do is use the
following:
 app.get()
 app.post()
 app.put()
 app.delete()

JavaScript Servers - Module 2 Page 16


Let’s see how they work in simple examples:
app.get('/', (req, res) => {
res.send('Hello World!')
})

app.post('/', (req, res) => {


res.send('Got a POST request')
})

app.put('/user', (req, res) => {


res.send('Got a PUT request at /user')
})

app.delete('/user', (req, res) => {


res.send('Got a DELETE request at /user')
})

As the first parameter, we must provide a path to the endpoint (for example ‘/’ or
‘/user’).
As the second, we need to provide a pair of parameters: request and response. We
can use the response parameter to respond to the client. We can chain different
methods to the same path – the code below does the same thing as the code
above:
app.route('/').get((req, res) => {
res.send('Hello World!')
}).post((req, res) => {
res.send('Got a POST request')
})

app.route('/user').put((req, res) => {


res.send('Got a PUT request at /user')
}).delete((req, res) => {
res.send('Got a DELETE request at /user')
})

JavaScript Servers - Module 2 Page 17


Let’s add a simple Hello World GET endpoint to the “/hello” path.
To do this, add the following code to a new “hello.js” file in the routes folder:
var express = require('express');
var router = express.Router();

/* GET hello world. */


router.get('/', function(req, res) {
res.send('Hello world');
});

module.exports = router;

For our application to see that code, we need to include this file in an “app.js” file.
Let’s create a helloRouter, similar to other routers:
var helloRouter = require('./routes/hello');

and connect it with the endpoint.


app.use('/hello', helloRouter);

Our final “app.js” file should look like this:


var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');


var usersRouter = require('./routes/users');
var helloRouter = require('./routes/hello');

var app = express();

// view engine setup


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

JavaScript Servers - Module 2 Page 18


app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/hello', helloRouter);

// catch 404 and forward to the error handler


app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ?
err : {};

// render the error page


res.status(err.status || 500);
res.render('error');
});

module.exports = app;

The client can pass parameters in the path. We can treat the element of the path as
a parameter by adding a “:” sign before it in the path. These values are stored in
the req.params object.
Let’s add new code to the “users.js” file, which handles
the/users/:userId/books/:bookId path and returns the req.params object:
router.get('/:userId/books/:bookId', (req, res) => {
res.send(req.params)
})

JavaScript Servers - Module 2 Page 19


Let’s open the http://localhost:3000/users/34/books/8989 link in the browser. We
should get the following result:

To understand what is happening there, look at the URL and path provided as the
first parameter of the get() method:
http://localhost:3000/users part is the default one for each handler in users.js file.
The rest is /34/books/8989:
 34 is passed as userId (there is “:” before userId in the path)
 books text is the same in both paths
 8989 is passed as the bookId variable.

The entire req.params object (responsible for storing route parameters) is displayed
as the result.
We can also provide a callback function (often named a route handler). We’ll pass it
as the function’s third parameter (after res) and name it “next”.

Let’s change our hello endpoint (“hello.js”) so that it returns a response in a


callback:
var express = require('express');
var router = express.Router();

/* GET hello world. */


router.get('/', function(req, res, next) {
console.log("will return response in a callback")
next()
}, (req, res) => {
res.send('Hello world');
});

JavaScript Servers - Module 2 Page 20


module.exports = router;

After restarting the application and accessing the http://localhost:3000/hello


address, we’ll see the following logs in our terminal:

While the response is the same.

READ

Page: Routing by ExpressJS.

WATCH

1. Video: What are routing parameters? (11m 55s) by Jamie


Pittman on LinkedIn Learning.
2. Video: What are route handlers? (4m 16s) by Jamie
Pittman on LinkedIn Learning.
3. Video: What are common methods for Express routing?
(3m 39s) by Jamie Pittman on LinkedIn Learning.
4. Video: What is route chaining in Express? (5m 25s) by
Jamie Pittman on LinkedIn Learning.

JavaScript Servers - Module 2 Page 21


ACTIVITY

Create a handler to the /hello/:name endpoint, where the user


provides us with their name as the route parameter, and we
return a “Hello :name!” greeting, using the callback function
(where :name is provided name).

We should get the following result:

Answer: Click here to reveal.

Middleware
Middleware functions have access to the request object, response object, and
next() function. We can use them to execute some actions before processing the
request:

JavaScript Servers - Module 2 Page 22


In the picture above, middleware functions are represented as green circles. For
example:
 first function can say hello to the user
 second logs the data
 third validates data in the req object
 fourth converts properties of the req object to the other types
 fifth one creates data and sends it using res object.

Middleware functions can perform the following tasks:


 execute any code
 make changes to the request and the response objects
 end the request-response cycle
 call the next middleware function in the stack.

Let’s see how to create a simple middleware function logging into the console.
First, temporarily add the following function into “app.js”:
const myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}

And then load this with the app.use() method:


app.use(myLogger);

JavaScript Servers - Module 2 Page 23


We see logs showing every time our app receives a request:

We can also use middleware functions to handle the errors. Error-handling


functions take four arguments instead of three. They are already used in our app.js
file:
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ?
err : {};

// render the error page


res.status(err.status || 500);
res.render('error');
});

Let’s temporarily add the following line:


console.error(err.stack)

inside the mentioned function in the app.js file, restart the application, and access a
non-existing endpoint (to trigger an error).

We should see this error in the console:

JavaScript Servers - Module 2 Page 24


There are three middleware functions built-in into Express:
1. express.static – serves static assets.
2. express.json – parses requests to JSON.
3. express.urlencoded – parses requests to URL-encoded.

We can also use third-party middleware to add new functionalities to Express. For
example, we can install a cookie parser with the command:
npm install cookie-parser

and then load it in the “app.js” file:


const cookieParser = require('cookie-parser')

// load the cookie-parsing middleware


app.use(cookieParser())

At https://expressjs.com/en/resources/middleware.html, we can see the list of


middleware maintained by the ExpressJS team and the list of the most popular
ones maintained by other teams:

JavaScript Servers - Module 2 Page 25


We can see that the ExpressJS team maintains the cookie parser we just installed.
After clicking its name, we’ll move to its own documentation page:
https://expressjs.com/en/resources/middleware/cookie-parser.html, where we can
find information such as how to install it and what functionalities it provides - with
code examples.

JavaScript Servers - Module 2 Page 26


READ

1. Page: Writing middleware for use in Express apps by


ExpressJS.
2. Page: Using middleware by ExpressJS.

WATCH

1. Video: What is middleware? by Jamie Pittman on


LinkedIn Learning.
2. Video: Out of the box: Built-in middleware with Express
(10m 21s) by Jamie Pittman on LinkedIn Learning.
3. Video: Handling errors: Utilizing middleware for errors
(6m 8s) by Jamie Pittman on LinkedIn Learning.
4. Video: Third-party middleware: Suggestions and how to
add it (2m 32s) by Jamie Pittman on LinkedIn Learning.

Static HTML
In this chapter, we’ll try to present an HTML page to the user instead of the strings
or JSON we have been presenting so far. Download any image and save it in the
images folder in your project (this picture will be used in the example:
https://www.freeimages.com/photo/green-frog-1361810).
Create an “index.html” file in the same place where the “app.js” file is located. This
file should then contain the following HTML code:
<!DOCTYPE html>
<html>
<head>
<title>App</title>

JavaScript Servers - Module 2 Page 27


</head>
<body>
<p>Hello!</p>
<img src="../public/images/frog.jpg">
</body>
</html>

It’s a straightforward HTML page with one text paragraph and a downloaded image.
(Change the picture name, “/frog.jpg”, in the HTML code to match the name of your
downloaded image).
We want to see this website on the starting endpoint (http://localhost:3000/). To do
this, open the “index.js” file in the router folder. We can use the router.sendFile
command to return the file (for example, an HTML page) in response.

However, as our HTML file is located in a different place, we need to use the path
module to access it. The path module is built-in into NodeJS, providing utilities for
working with file and directory paths. Let’s change the file so it looks as follows:
var express = require('express');
var path = require('path');
var router = express.Router();

/* GET home page. */


router.get('/', function(req, res, next) {
res.sendFile(path.join(__dirname, '..', 'index.html'))
});

module.exports = router;

and check the results in the browser:

JavaScript Servers - Module 2 Page 28


The website is created, although the image isn’t displayed correctly. This is
because the router doesn’t know where to look for the image. We can specify this
with the use() method. Files are named dependent on where the server file is
located (app.js).
We can access the path to the directory where the index.js file is located with the
__dirname variable. Then, we add ‘..’ text to specify that we want to access its
parent directory – the same directory where the “app.js” and “index.html” files are
located.

We should use the express.static() middleware function to specify that we’re


looking for static resources.
So, to tell the router file where to look for the image, we need to add the following
code to the index.js file:
router.use(express.static(path.join(__dirname, '..')));

And final “index.js” file:


var express = require('express');
var path = require('path');
var router = express.Router();

/* GET home page. */


router.use(express.static(path.join(__dirname, '..')));

JavaScript Servers - Module 2 Page 29


router.get('/', function(req, res, next) {
res.sendFile(path.join(__dirname, '..', 'index.html'))
});

module.exports = router;

Now our HTML page works correctly!

Static resources aren’t only pictures. It can also be CSS styles, for example.
Let’s add a new stylesheet in which we’ll do the following:
 make the text green
 enlarge the fonts
 set the size of the image
 centre both the paragraph and the image

JavaScript Servers - Module 2 Page 30


Let’s create a “mainPage.css” file in the public/stylesheets folder for this styling:
p {
font-size: 20px;
color: green;
text-align: center;
}

img {
width: 200px;
margin: auto;
display: block;
}

This CSS file format should be familiar. Now link it in the “index.html” file as a
stylesheet:
index.html:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<link rel="stylesheet"
href="../public/stylesheets/mainPage.css">
</head>
<body>
<p>Hello!</p>
<img src="../public/images/frog.jpg">
</body>
</html>

We see that the style changes were applied:

JavaScript Servers - Module 2 Page 31


Notice that changes were applied without restarting the server. Every time we make
changes to the application interface, they don’t require us to restart the server. We
still need to restart it when we make changes connected to the server. For
example, when adding a new endpoint to the application.
Since we know how to connect HTML to endpoints, we can create well-structured
and attractive websites using routing.

WATCH

1. Video: Building from an HTML page or template (4m


23s) by Daniel Khan on LinkedIn Learning.
2. Video: Serving HTML pages and static content (4m 43s)
by Daniel Khan on LinkedIn Learning.

JavaScript Servers - Module 2 Page 32


ACTIVITY

We learnt how to add static resources such as images and


styles. JavaScript programs can also be static resources. In this
activity, create a button with the label “Click me”, place it under
the frog picture and bind a “hello()” function to it. The function
should display a greeting to the user in an alert. This function
should be placed in a separate file.

Once the button is clicked, you should get the following results:

Source code: Click here to reveal


public/javascripts/greetings.js.

Source code: Click here to reveal index.html.

Introduction to template engines


We are already able to create Express applications. However, template engines
help us improve these web applications.

JavaScript Servers - Module 2 Page 33


There are two main reasons to use template engines:
1. We are able to use variables in our HTML code and change their values
dynamically.
2. We can split our website into reusable parts. For example, we might use
the navbar on every endpoint, but with template engines, we can
separate it into a distinct file and attach it to every file when needed.
This way, we only need to edit the navbar template instead of every file,
including the navbar.

ExpressJS supports numerous template engines. The default and most


popular is Jade, but we’ll learn Embedded JavaScript (commonly referred to
as EJS) as it’s simpler and beginner-friendly. There are several other
template engines for JavaScript, such as:
1. Pug
2. Mustache.js
3. Dust.js
4. Nunjucks

See the full list of template engines linked in the READ section.
All these engines are easy to install with NPM or Express Generator.

READ

1. Page: Template engines by ExpressJS.


2. Page: Using template engines with Express by
ExpressJS.
3. Article: Comparing JavaScript Templating Engines: Jade,
Mustache, Dust and More by Alex Gorbatchev on
StrongLoop by IBM.

JavaScript Servers - Module 2 Page 34


WATCH

Video: Template engines and Express (1m 49s) by Daniel Khan


on LinkedIn Learning.

Installation of EJS
We can install EJS by calling the command:
npm install ejs

or adding it with an Express generator while creating a new project.


For example, let’s create a new project called “ejsapp” with a command, including
the “ejs” command:
express ejsapp --ejs

JavaScript Servers - Module 2 Page 35


EJS enables us to use JavaScript in any place of the HTML files we want to use.
These files will no longer have a ‘.html’ extension but ‘.ejs’.
We can also install a VS Code extension to help us with writing EJS code:

Let’s analyse the created “index.ejs” file:

JavaScript Servers - Module 2 Page 36


<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>

It uses the “title” variable in three places. This variable is set in the “index.js” router
file:
var express = require('express');
var router = express.Router();

/* GET home page. */


router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});

module.exports = router;

In the object, we set the value of the title variable to Express’.

Our running web page look as follows:

JavaScript Servers - Module 2 Page 37


After changing the title value (and restarting the application), all three values
change:
res.render('index', { title: 'New title' });

READ

Website: https://ejs.co/

JavaScript Servers - Module 2 Page 38


WATCH

1. Video: Getting to know the EJS template engine (3m 7s)


by Daniel Khan on LinkedIn Learning.
2. Video: Rendering the index page with EJS (2m 41s) by
Daniel Khan on LinkedIn Learning.

Templating - Layout
In this chapter, we’ll learn how to divide one big page into multiple smaller ones
(called partials). Partials contain only code about a certain component, for example,
the navbar. We can compare them to classes in Object Oriented Programming.
Instead of having huge HTML files, we should export independent parts to the
partials and include them in the main file.
To demonstrate this, we need a more complex HTML page. Let’s add Bootstrap
and jQuery to our application. Place “jquery-3.6.0.js” and “bootstrap.bundle.min.js”
in the public/javascripts folder and “bootstrap.min.css” in the public/stylesheets
folder (we downloaded these files multiple times in previous chapters. If you don’t
have them yet, you can download them from: https://getbootstrap.com/ and
https://jquery.com/download/).
Let’s replace our current “index.ejs” file with the following code:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,
initial-scale=1">
<!-- Bootstrap CSS -->
<link href="../public/stylesheets/bootstrap.min.css"
rel="stylesheet">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-
icons@1.9.1/font/bootstrap-icons.css">

JavaScript Servers - Module 2 Page 39


<title>Hello, world!</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-
light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<div class="collapse navbar-collapse"
id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="#"><i
class="bi bi-1-circle"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-2-
square"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-3-
circle-fill"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"
id="liveToastBtn"><i class="bi bi-4-square-fill"></i></a>
</li>
</ul>
</div>
</div>
</nav>
<div id="carouselExampleControls" class="container p-5
carousel carousel-dark slide" style="width: 300px" data-bs-
ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img
src="https://i.pinimg.com/originals/c9/40/a3/c940a3dae3900d10
0990e5b697ef196c.jpg" class="d-block w-100" alt="Pikachu 1">
</div>

JavaScript Servers - Module 2 Page 40


<div class="carousel-item">
<img
src="https://cdn.pixabay.com/photo/2016/09/09/11/59/pokemon-
1656997_960_720.png" class="d-block w-100" alt="Pikachu 2">
</div>
</div>
<button class="carousel-control-prev" type="button"
data-bs-target="#carouselExampleControls" data-bs-
slide="prev">
<span class="carousel-control-prev-icon "></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button"
data-bs-target="#carouselExampleControls" data-bs-
slide="next">
<span class="carousel-control-next-icon"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
<div class="text-center">
<div class="btn-group" role="group" aria-label="Basic
mixed styles example">
<button type="button" class="btn btn-
danger">Left</button>
<button type="button" class="btn btn-
warning">Middle</button>
<button type="button" class="btn btn-
success">Right</button>
</div>
</div>
<div class="position-fixed bottom-0 end-0 p-3"
style="z-index: 11">
<div id="liveToast" class="toast hide" role="alert"
aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-
dismiss="toast" aria-label="Close"></button>

JavaScript Servers - Module 2 Page 41


</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>
<script
src="../public/javascripts/bootstrap.bundle.min.js"></script>
<script src="../public/javascripts/jquery-
3.6.0.js"></script>
<script>
$(document).ready(function(){
$("#liveToastBtn").click(function(){
$("#liveToast").toast("show");
});
});
</script>
</body>
</html>

We also need to tell our router where to look for files by adding
router.use(express.static(path.join(__dirname, '..')));

to the “index.js” file.


Let’s restart the application. If everything was done correctly, the
http://localhost:3000/ page should look like this:

JavaScript Servers - Module 2 Page 42


Our HTML file is now quite complex. So, let’s split it into smaller files.
Create the “partials” folder in the “views” folder.

In “partials”, create the following files.


 navbar.ejs

<nav class="navbar navbar-expand-lg navbar-light bg-light">


<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<div class="collapse navbar-collapse"
id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="#"><i class="bi
bi-1-circle"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-2-
square"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#"><i class="bi bi-3-
circle-fill"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" id="liveToastBtn"><i
class="bi bi-4-square-fill"></i></a>
</li>
</ul>
</div>
</div>
</nav>

 carousel.ejs

<div id="carouselExampleControls" class="container p-5


carousel carousel-dark
slide" style="width: 300px" data-bs-ride="carousel">
<div class="carousel-inner">

JavaScript Servers - Module 2 Page 43


<div class="carousel-item active">
<img
src="https://i.pinimg.com/originals/c9/40/a3/c940a3dae3900d10
0990e5b697ef196c.jpg" class="d-block w-100" alt="Pikachu 1">
</div>
<div class="carousel-item">
<img
src="https://cdn.pixabay.com/photo/2016/09/09/11/59/pokemon-
1656997_960_720.png" class="d-block w-100" alt="Pikachu 2">
</div>
</div>
<button class="carousel-control-prev" type="button" data-
bs-target="#carouselExampleControls" data-bs-slide="prev">
<span class="carousel-control-prev-icon "></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-
bs-target="#carouselExampleControls" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
<span class="visually-hidden">Next</span>
</button>
</div>

 button-group.ejs

<div class="text-center">
<div class="btn-group" role="group" aria-label="Basic
mixed styles example">
<button type="button" class="btn btn-
danger">Left</button>
<button type="button" class="btn btn-
warning">Middle</button>
<button type="button" class="btn btn-
success">Right</button>
</div>
</div>

 toast.ejs

JavaScript Servers - Module 2 Page 44


<div class="position-fixed bottom-0 end-0 p-3" style="z-
index: 11">
<div id="liveToast" class="toast hide" role="alert" aria-
live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-
dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>

 scripts.ejs

<script
src="../public/javascripts/bootstrap.bundle.min.js"></script>
<script src="../public/javascripts/jquery-3.6.0.js"></script>
<script>
$(document).ready(function(){
$("#liveToastBtn").click(function(){
$("#liveToast").toast("show");
});
});
</script>

 And change our “index.ejs” code to:

<!doctype html>
<html lang="en">
<head>
<title>Hello, world!</title>
</head>
<body>
<%- include('./partials/navbar.ejs') %>
<%- include('./partials/carousel.ejs') %>
<%- include('./partials/button-group.ejs') %>

JavaScript Servers - Module 2 Page 45


<%- include('./partials/toast.ejs') %>
<%- include('./partials/scripts.ejs') %>
</body>
</html>

With the <%- %>, we tell the template engine we’ll use the external values there.
With the include function, we add other templates to our HTML file.

Restart the application and make sure that your html elements are loaded (The
styling will not be correct. That will be fixed in the Activity below).

Thanks to these changes, adding a picture in the carousel means we don’t have to
scroll through the entire page’s code. Instead, we need to find the “carousel.ejs” file
and edit it. Moreover, we can reuse this file in different files just by including it in the
same way we did here.

Therefore, by changing only one file, we can affect all places its code is used, just
like in Object Oriented Programming.

WATCH

1. Video: Create a site-wide layout (6m 54s) by Daniel


Khan on LinkedIn Learning.
2. Video: Using partials with EJS (4m 56s) by Daniel Khan
on LinkedIn Learning.

JavaScript Servers - Module 2 Page 46


ACTIVITY

First, create a new partial that will contain the previous <head>
code from the index.ejs (before we split it into partials). Add it
in the <head> section of the current “index.ejs” file.

Then create another new partial containing the “Hello World!”


paragraph, adding it between the carousel and button group in
the “index.ejs” file. This new partial needs styling, so create a
new CSS file for this helloWorld partial (which will centre the
text) and link it in the correct place so the main page looks as
follows:

Source code: Click here to reveal /views/partials/helloWorld.ejs.

Source code: Click here to reveal /views/index.ejs.

Source code: Click here to reveal


/public/stylesheets/helloWorld.css.

Source code: Click here to reveal /views/partials/head.ejs.

JavaScript Servers - Module 2 Page 47


Templating - Variables
As we saw in the installation chapter, we can pass variables within an object and
use them in “.ejs” files. We can also use JavaScript functions and loops.
Let’s create a “helloList.ejs” file in the partial folder.
<ul>
<%users.forEach(function(user){ %>
<li>Hello <%- user %></li>
<% }); %>
</ul>

We want to list users. As input, we should get the user’s array. We can use the
forEach method on this array to generate code for each user.
We also want to add a Hello message to the list. Notice the difference in tags. The
“<%” tag doesn’t output values to the HTML template, while “<%-“ does. In other
words, code inside the “<%” tag will still execute, but its result won’t be visible in the
browser. For example, writing “<% user %>” would not display the user name.
In EJS, we can use the following tags:
 <% - allows us to use ‘scriptlets’ – small programs containing logic
similar to JavaScript. For example, we use them to create a start if
statement or loop inside the view (such as the forEach loop used above).
It has no output.
 <%_ - the same as <% tag, but strips all whitespace before execution.
 <%= - outputs value into the template HTML escaped-characters, which
have a special function in HTML and are represented as HTML code,
useful when we provide data directly to the user.
 <%- - outputs unescaped value into the template HTML, useful to include
other files (for example, partials). It can also be used to display data if
we know that data doesn’t contain special characters.

JavaScript Servers - Module 2 Page 48


 %> - standard ending tag
 Less common tags that we won’t use in this course:

o <%# - comment tag without execution and output


o <%% - tag outputting ‘<%’
o -%> - trim mode ending tag – removes whitespaces from the following
line
o _%> - ‘whitespace slurping’ ending tag - removes whitespaces from all
following lines after it.
Let’s add this “helloList.ejs” partial to our “index.ejs” file:
<!doctype html>
<html lang="en">
<head>
<%- include('./partials/head.ejs') %>
<title>Hello, world!</title>
</head>
<body>
<%- include('./partials/navbar.ejs') %>
<%- include('./partials/carousel.ejs') %>
<%- include('./partials/button-group.ejs') %>
<%- include('./partials/helloList.ejs', {users: ["John
Doe", "Mark White", "Barbara Smith"]}) %>
<%- include('./partials/toast.ejs') %>
<%- include('./partials/scripts.ejs') %>
</body>
</html>

We can either set the user’s array here with static data (as the second parameter of
the include function) or get dynamic data from the router.
Let’s see the result:

JavaScript Servers - Module 2 Page 49


The list was created correctly.
We know that we can take parameters from the user and pass them in the router
parameters. Therefore, let’s add a second method to our routes/index.js router file:
router.get('/:newUser', function(req, res, next) {
res.render('index', { newUser: req.params.newUser });
});

And change our include in views/index.ejs to:


<%- include('./partials/helloList.ejs', {users: ["John
Doe", "Mark White", "Barbara Smith", newUser]}) %>

To pass the parameter “Mark”, let’s navigate to http://localhost:3000/Mark.

JavaScript Servers - Module 2 Page 50


We see that the user provided (“Mark”) in the route parameters was added correctly
to the list.

READ

Page: Docs by EJS.

JavaScript Servers - Module 2 Page 51


WATCH

1. Video: Template variables in more detail (5m 57s) by


Daniel Khan on LinkedIn Learning.
2. Video: Looping through lists in templates (11m 33s) by
Daniel Khan on LinkedIn Learning.
3. Video: Creating a list page (6m 4s) by Daniel Khan on
LinkedIn Learning.
4. Video: Using parameter routes (7m 9s) by Daniel Khan
on LinkedIn Learning.

JavaScript Servers - Module 2 Page 52


ACTIVITY

Update the code so that the final list only displays users with an
odd index in the user’s array:

Source code: Click here to reveal helloList.ejs.

What did I learn in this lesson?


This lesson provided the following insights:
 ExpressJS and how to create an Express application.
 How routing works in ExpressJS.
 Middleware functions and what they are.
 How to serve static HTML files with Express.

JavaScript Servers - Module 2 Page 53


 Template engines and how to use EJS.
 How to template Express with EJS.

References
OpenJS Foundation (2017). Express - Node.js web application framework. [online]
Expressjs.com. Available at: https://expressjs.com/.
LinkedIn. (n.d.). Express Essential Training Online Class | LinkedIn Learning,
formerly Lynda.com. [online] Available at:
https://www.linkedin.com/learning/express-essential-training-14539342.
Stack Overflow. (n.d.). angular - why am I suddenly getting: ng : File
C:\Users\d\AppData\Roaming\npm\ng.ps1 cannot be loaded. [online] Available at:
https://stackoverflow.com/questions/72863930/why-am-i-suddenly-getting-ng-file-c-
users-d-appdata-roaming-npm-ng-ps1-canno.
www.freeimages.com. (n.d.). Green frog Free Photo Download. [online] Available
at: https://www.freeimages.com/photo/green-frog-1361810.
LinkedIn. (n.d.). Building a Website with Node.js and Express.js Online Class |
LinkedIn Learning, formerly Lynda.com. [online] Available at:
https://www.linkedin.com/learning/building-a-website-with-node-js-and-express-js-3.
ejs.co. (n.d.). EJS -- Embedded JavaScript templates. [online] Available at:
https://ejs.co/#docs.
Otto, M. (n.d.). Bootstrap. [online] Getbootstrap.com. Available at:
https://getbootstrap.com/.
JS Foundation - js.foundation (2019). Download jQuery | jQuery. [online]
Jquery.com. Available at: https://jquery.com/download/.
strongloop.com. (n.d.). StrongLoop - Comparing JavaScript Templating Engines:
Jade, Mustache, Dust and More. [online] Available at:
https://strongloop.com/strongblog/compare-javascript-templates-jade-mustache-
dust/.

JavaScript Servers - Module 2 Page 54


2.1. Lesson task - ExpressJS and
HTML

The task
Part 1

In this lesson, we learnt about ExpressJS. In this part of the task, you must create
an “/add” endpoint, which takes two numbers (as parameters) and returns their
sum.
For example, on path http://localhost:3000/add/1/5, we should get 6 as a result.
Source code: Click here to reveal add.js.
Source code: Click here to reveal app.js.

Part 2

We also learnt about template engines. In this part of the task, you need to
create an application that has three endpoints:
 / - main empty page
 /number – generates a random number from 1 to 10 and returns to the
client
 /tellme/:routeParamString – returns the string provided in route
parameters, assuming that there will always be a route parameter
provided.
For example, endpoint “/tellme/wizard” should return “wizard” in
response.

These endpoints must have a “Hello” text at the top. Please use partial to add this.
Source code: Click here to reveal app.js.
Source code: Click here to reveal index.js.
Source code: Click here to reveal index.ejs.
Source code: Click here to reveal number.js.

JavaScript Servers - Module 2 Page 55


Source code: Click here to reveal number.ejs.
Source code: Click here to reveal tellme.js.
Source code: Click here to reveal tellme.ejs.
Source code: Click here to reveal partials/hello.ejs.

2.2. Lesson - Web sockets and


ExpressJS application

Introduction

We already know how to create applications with ExpressJS and how to create a
dynamic front-end for them. This lesson will focused on the backend and
connection side of the application.
We’ll learn how to use web sockets to create a real-time chat application. For this,
we’ll use the Socket.IO library, one of the most popular libraries to manage web
sockets for ExpressJS.
The following chapters will show us why and how to use web sockets, how to install
Socket.io and create a real-time chat application for many users with many rooms.

JavaScript Servers - Module 2 Page 56


After that, we’ll know how to use ExpressJS to create high-quality applications with
servers. We’ll also learn how to improve the application front-end using template
engines and how to manage real-time applications with web sockets.
We’ll try to apply our knowledge in practice. While we’ll mainly use current skills,
we’ll also look at some things in a more detailed way. For example, so far, we have
been primarily implementing GET requests. Here, we’ll explore managing POST
requests.
The primary purpose is to ensure we have complete knowledge of ExpressJS
basics and that we can use this technology freely to create our own applications in
future.

Learning outcomes
In this lesson, we are covering the following knowledge learning
outcome:
 The candidate has knowledge of concepts, processes, and tools of
server-based JavaScript solutions.

In this lesson, we are covering the following skill learning outcome:


 The candidate can apply vocational knowledge of server-based
JavaScript solutions to practical and theoretical solutions.

In this lesson, we are covering the following general competence


learning outcome:
 The candidate understands the ethical principles that apply when
developing, deploying and maintaining server-based JavaScript
solutions.

Introduction to web sockets


Web sockets, similar to HTTP, are used for communication between the client and
server. The main difference is that the web socket establishes a connection

JavaScript Servers - Module 2 Page 57


between the client and server and then keeps it. In contrast, the HTTP connection
between the client and server is terminated after the server responds to the client.
As we don’t need to waste time establishing a connection, communication via web
sockets is much faster than via HTTP.
Because of that, web sockets are perfect for scenarios where we need a stable,
permanent connection with the client, such as chat applications, games, or any
other real-time applications.

Source: https://www.geeksforgeeks.org/what-is-web-socket-and-how-it-is-
different-from-the-http/

Source: https://www.geeksforgeeks.org/what-is-web-socket-and-how-it-is-
different-from-the-http/

JavaScript Servers - Module 2 Page 58


There are several packages to work with web sockets in NodeJS, such as:
 Socket.io;
 Totaj.js;
 µWebSockets;
 Faye;
 and many more.

Other web technologies have their web socket packages as well. For example,
while working with ASP.NET apps, we would use Microsoft’s SignalR.
In this course, we’ll use Socket.io, as it is well-known in the NodeJS community for
being performant and reliable.

READ

Page: The WebSocket API (WebSockets) by MDN Web Docs.

Installing Socket.IO
Applications generated by Express Generator by default use an HTTP connection.
While it is possible to change this, it is complex and doesn’t help much with
understanding web sockets.
Because of that, we’ll create a project without Express Generator in this lesson.
Let’s create the folder “socketapp”, enter this folder, and type the following
commands in the terminal:
npm init

and use the default settings. This command should create a “package.json” file.
Then update this file with new dependencies:
npm install express socket.io

JavaScript Servers - Module 2 Page 59


After that, we should see express and socket.io as dependencies in our
“package.json” file.
Let’s create “index.js” and “index.html” files (in the same place where
“package.json” is). For now, we’ll use templates from the official socket.io getting
started tutorial (linked in the READ section), of which the code is provided below:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
body { margin: 0; padding-bottom: 3rem; font-family: -
apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif; }

#form { background: rgba(0, 0, 0, 0.15); padding:


0.25rem; position: fixed; bottom: 0; left: 0; right: 0;
display: flex; height: 3rem; box-sizing: border-box;
backdrop-filter: blur(10px); }
#input { border: none; padding: 0 1rem; flex-grow: 1;
border-radius: 2rem; margin: 0.25rem; }
#input:focus { outline: none; }
#form > button { background: #333; border: none;
padding: 0 1rem; margin: 0.25rem; border-radius: 3px;
outline: none; color: #fff; }

#messages { list-style-type: none; margin: 0; padding:


0; }
#messages > li { padding: 0.5rem 1rem; }
#messages > li:nth-child(odd) { background: #efefef; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
<input id="input" autocomplete="off"
/><button>Send</button>
</form>

JavaScript Servers - Module 2 Page 60


<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
</body>
</html>

index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {


res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {


console.log('a user connected');
});

server.listen(3000, () => {
console.log('listening on *:3000');
});

We’ll analyse these files in the next chapter. For now, all we need to do is check if
the http://localhost:3000/ page loads correctly (when we see the input field at the
bottom) and if we see “a user connected” log in the console:

JavaScript Servers - Module 2 Page 61


If these are present, we can move to the next chapter.

READ

Page: Get Started by Socket.IO.

WATCH

Video: Base NPM setup (7m 18s) by Emmanuel Henri on


LinkedIn Learning.

JavaScript Servers - Module 2 Page 62


ACTIVITY

We can see “a user connected” communicated in the console,


but when exactly does it show in the execution of our web
application? Experiment with the app to discover this. Check
sockets’ IDs by logging them with the function (in the same
place as the “a user connected” log):

When are IDs different?

Answer: Click here to reveal.

Basic socket communication


Right now, code connected to Socket.IO is in two places:
In “index.js”, we initialise the server and make it listen to the “connection” event.
The connection event is emitted whenever a new socket is created – when users
open the link in the browser. Because of that, we see the “a user connected” log in
the console.
The second place is the script section in the “index.html” file. This code causes the
creation of the new socket on the page to reload.
Knowing where the Socket.IO code is located, let’s try to exchange messages
between the client and server. The “connection” event is the default, but we can
create our own. We can manage the server socket from the “index.js” file. Let’s
emit a new “server message” event after the client’s connection:
io.on('connection', (socket) => {
socket.emit('server message', { server: 'any messages for
me?'});
});

JavaScript Servers - Module 2 Page 63


And handle it in the client socket (in index.html)
<script>
var socket = io();
socket.on('server message', (data) => {
console.log(data);
})
</script>

We use the socket.emit() function to emit the new event and socket.on() function to
listen for them. Right now, the server sends the message “any messages for me”,
and the client displays this in the web console:

Communication from the server to the client is working correctly. Now let’s try to
implement the other side. We want to send the message provided by the user to

JavaScript Servers - Module 2 Page 64


the server and display it in the console. On the server part, it will be similar to what
we have been seeing so far:
index.js:
io.on('connection', (socket) => {
socket.emit('server message', { server: 'any messages for
me?'});
socket.on('chat message', (msg) => {
console.log('message: ' + msg);
});
});

Our server’s socket is listening for a “chat message” event and, after getting one,
displays a message in the console.
On the client side, it is different from before.

We need to get data from the input first and then, after the button click, send it to
the server. We’ll use the following code to do that:
<script>
var socket = io();

socket.on('server message', (data) => {


console.log(data);
})

var form = document.getElementById('form');


var input = document.getElementById('input');

form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
</script>

JavaScript Servers - Module 2 Page 65


When our form is submitted, we check if the input value is correct and send it to the
server using the client socket. After that, we reset the input value.
Let’s create a new message:

And send it to the server:

Here we see that the server has received the message.

Therefore, our basic communication between the client and server is working
correctly.

READ

Page: Integrating Socket.IO by Socket.IO.

WATCH

1. Video: Socket server with NPM and Express (13m 41s)


by Emmanuel Henri on LinkedIn Learning.
2. Video: Coding our front-end HTML (9m 56s) by
Emmanuel Henri on LinkedIn Learning.

JavaScript Servers - Module 2 Page 66


ACTIVITY

Experiment with the app and check sockets’ IDs by logging


them with the function:

Once after connection and a second time while getting a chat


message.

When are IDs different? Compare results to the exercise in the


last chapter.

Answer: Click here to reveal.

Events
We already know how to use simple events to implement communication between
the client and server. However, there are usually multiple clients in our chatroom,
and we want many users to be able to chat. We also want users to see messages
from others. As clients aren’t connected, the server needs to implement this
functionality.
There are two ways to solve this problem:
1. We can use the io.emit() method, which sends a message to everyone.
2. We can use broadcast – the server can send a message to every
connected client besides the one who sent it. All we need to do is add a
broadcast flag before the emit() function – socket.broadcast.emit().

We want our users to see their own messages as well, so we’ll use the first option:
index.js:

JavaScript Servers - Module 2 Page 67


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

The server is sending messages correctly. Now we only need to handle them on
the client side:
<script>
var socket = io();

socket.on('server message', (data) => {


console.log(data);
})

var form = document.getElementById('form');


var input = document.getElementById('input');

form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
socket.on('chat message', function(msg) {
var item = document.createElement('li');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
</script>

We use DOM methods to update the list after the event. In the end, the window is
scrolled to the most recent message.

JavaScript Servers - Module 2 Page 68


Let’s test it. Open http://localhost:3000/ in one window, type the “Hello” message,
open it in a second window, and type “Hi”. We should get this result:

READ

1. Page: Emitting events by Socket.IO.


2. Page: Broadcasting by Socket.IO.

WATCH

1. Video: Code and test our first interactions (7m 10s) by


Emmanuel Henri on LinkedIn Learning.
2. Video: Events: Connect and disconnect (5m 24s) by
Emmanuel Henri on LinkedIn Learning.

JavaScript Servers - Module 2 Page 69


ACTIVITY

Right now, clients communicate only with a “chat message”


event. Create a second type of message – “chat2 message” and
make the client have a 50% chance of either using “chat
message” or “chat2 message” to communicate. This way, while
having three clients, two of them might communicate with “chat
message” and the last one with “chat2 message”. Clients should
see only messages connected to the event they use:

Hint: You can create a copy of the index.html file, change it to


use the “chat2 message” event, and randomly render either the
index.html file or its copy in the endpoint handler.

Answer: Click here to reveal index.js.

Answer: Click here to reveal index2.html.

Namespaces and rooms


With Socket.IO, we can create namespaces to authorise and separate users.
Namespace is a communication channel that allows us to split the logic of our

JavaScript Servers - Module 2 Page 70


application over a single shared connection. Each namespace has its own event
handlers, rooms and middleware.
In the case of our application, we can assume, for example, that some chats are
available only for users who confirmed their email, or we can create a separate
namespace for admins.
Even though the client and server can use many namespaces, communication is
still always with the same pipe, as presented in the picture below.

To create a new namespace, we need to use the io.of() method and then replace
all uses of “io” with provided as the parameter name. Let’s see this in an example:
Changes in index.js:
const admin = io.of("/admin");

admin.on('connection', (socket) => {


socket.on('chat message', (msg) => {
admin.emit('chat message', msg);
});

Changes in index.html:
var socket = io('/admin');

After the application of these changes, the client and server are communicating by
admin namespace.
Room is an arbitrary channel that sockets can join or leave with methods:
socket.join("some room")

And

JavaScript Servers - Module 2 Page 71


socket.leave("some room")

Then namespace calls only some rooms using the to() method:
admin.to("some room").emit("some text");

Let’s apply this knowledge to our application. We want to have two rooms: room1
and room2. Let’s create “room1.html” and “room2.html” files containing the current
code from “index.html”.
Let’s replace “index.html” with this code:
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
</style>
</head>
<body>
<p>Select room</p>
<ul>
<a href="/room1">Room 1</a>
<a href="/room2">Room 2</a>
</ul>

JavaScript Servers - Module 2 Page 72


</body>
</html>

And add routing in “index.js”:


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

app.get('/room2', (req, res) => {


res.sendFile(__dirname + '/room2.html');
});

We already have the front end ready to work, but messages from all rooms are
visible in each room.
We’ll add the functionality that after a user enters or leaves a room, the message is
displayed.
Let’s start with the “index.js” server file. Use this connection code:
admin.on('connection', (socket) => {
socket.on('join', (data) => {
socket.join(data.room);
admin.in(data.room).emit('chat message', `New user
joined ${data.room} room!`);
})

socket.on('chat message', (data) => {


admin.in(data.room).emit('chat message', data.msg);
});

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

Whenever a client joins the room, a message reading something similar to, "New
user joined this room!" is sent to all clients in that room. Whenever the client sends

JavaScript Servers - Module 2 Page 73


a message, it is sent to all clients in that room, and whenever the client
disconnects, a "user disconnected" message is also sent to all clients in room.
Now let’s adapt our HTML code:
room1.html:
<script>
const room = 'room1';
var socket = io('/admin');

socket.on('server message', (data) => {


console.log(data);
})

var form = document.getElementById('form');


var input = document.getElementById('input');

form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
var msg = input.value;
socket.emit('chat message', { msg, room });
input.value = '';
}
});
socket.on('chat message', function(msg) {
var item = document.createElement('li');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});

socket.on('connect', () => {
socket.emit('join', { room: room });
})
</script>

In the first line, we set the room to room1. Whenever we emit a message, we add
room as the parameter in the object. When we enter the room, we send a join

JavaScript Servers - Module 2 Page 74


event. (Entering a room invokes a “connect” event. On the “connect” event, we emit
a “join” event).
The only difference in room2.html is the line:
const room = 'room2';

To check how our app works, we select a room on the main page:

We see that users in different rooms don’t see each other’s messages:

READ

Page: Rooms by Socket.IO.

JavaScript Servers - Module 2 Page 75


WATCH

1. Video: Introduction and namespace setup (4m 32s) by


Emmanuel Henri on LinkedIn Learning.
2. Video: Introduction to rooms and setup (7m 8s) by
Emmanuel Henri on LinkedIn Learning.
3. Video: Set up multiple rooms (8m 10s) by Emmanuel
Henri on LinkedIn Learning.

Web sockets with template engine


In our previous project, files “room1.html” and “room2.html” were highly similar. The
only difference between them was one line.
Fortunately, we can fix that and use only one file with the application of template
engines. Let’s start with adding EJS to our project:
npm install ejs

Let’s add the path module and EJS view engine in our “index.js” server file:
const path = require('path')
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

Now delete file “room2.html” and rename “room1.html” to “room.ejs”.


Find the line of code in “room.ejs” that sets the room and change it to the following:
const room = '<%-room%>';

We’ll pass the room name as the parameter.

JavaScript Servers - Module 2 Page 76


Let’s go back to the “index.js” file. The render() method comes with the same result
as the sendFile() method, but it works with template engines. Let’s use the render
method and pass the room name as the parameter:
app.get('/room1', (req, res) => {
res.render(__dirname + '/room.ejs', {room: 'room1'});
});

app.get('/room2', (req, res) => {


res.render(__dirname + '/room.ejs', {room: 'room2'});
});

We see that the website works exactly the same; however, it now makes use of the
EJS template engine.

ACTIVITY

Using route parameters, you must enable the user to use any
room, not only room1 and room2. For example, after opening
http://localhost:3000/anythingTheyType in the browser, they
will join anythingTheyType room.

After finishing the exercise, revert the changes (in the next
chapters, we don’t want the user to be able to use any room).

Answer: Click here to reveal.

JavaScript Servers - Module 2 Page 77


Adding rooms with request
When we have only one template, creating new rooms is much easier. We only
need the path to the endpoint, which is later passed to the template.
Thanks to that, we can think of adding new functionality to our application: creating
dynamic rooms with POST requests.
We’ll create a POST request (for example, with Postman) to /newroom endpoint,
with the room name in JSON body, and then handle it in the “index.js” file.
We’ll use the app.post() method to handle the POST request. We need to create a
JSON parser and pass it to the method as the second parameter:
var bodyParser = require('body-parser')
var jsonParser = bodyParser.json()
app.post('/newroom', jsonParser, (req, res) => {
/*code to handle*/
})

We need to get the room parameter from the body, initialise the handling of the
GET request on the new endpoint and return JSON in the response:
app.post('/newroom', jsonParser, (req, res) => {
const room = req.body.room;
app.get('/' + room, (req, res) => {
res.render(__dirname + '/room.ejs', {room:
room});
});
res.send({
'room': room
});
})

Let’s run the application and send a POST request with Postman:

JavaScript Servers - Module 2 Page 78


We can access it at http://localhost:3000/newroom!

JavaScript Servers - Module 2 Page 79


ACTIVITY

Right now, every odd message has a darker background - in


style element, line:

While creating a new room, enable the client to set this colour.
The client should provide it as the “color” string property in the
request’s body. This property isn’t mandatory. Your code
should still handle requests with no “color” property provided
(i.e., the colour parameter shouldn’t be required).

You can assume that it is correct if the user provides data (you
don’t need to focus on data validation here). Fix that by making
changes only in the /newroom POST handler and in the view file
“room.ejs”. Ensure that room1 and room2 are working properly
while not making changes in their GET handlers - they should
stay this way:

After getting the request from the client:

We should get the result:

JavaScript Servers - Module 2 Page 80


Updating room list
While we can add new rooms, they aren’t visible on the main list page. In this
chapter, we’ll try to fix that with a template engine.
Let’s start with renaming the “index.html” file to “index.ejs” and replacing the listing
code part with:
<ul>
<%rooms.forEach(function(room){ %>
<a href="/<%-room%>"><%- room %></a>
<% }); %>
</ul>

The room array will be provided as a parameter. For each room in the array, we
dynamically create a link.
Now let’s move to “index.js”.

Let’s initialise the room array with ‘room1’ and ‘room2’:


let rooms = ['room1', 'room2']

While creating a new room, we add the new name to the array. We do this by
pushing new room to the rooms array (6th line in the code below):
app.post('/newroom', jsonParser, (req, res) => {
const room = req.body.room;
app.get('/' + room, (req, res) => {
res.render(__dirname + '/room.ejs', {room:
room});
});
rooms.push(room);
res.send({
'room': room
});
})

JavaScript Servers - Module 2 Page 81


The last thing we need to do is update the name of the main page template and
pass the rooms array as the parameter:
app.get('/', (req, res) => {
res.render(__dirname + '/index.ejs', {rooms: rooms});
});

Now we can check if the application is working correctly. Let’s restart the
application, create a new room called “newRoom” (by sending a POST request with
Postman) and open the link: http://localhost:3000/.

We see that the new room is visible.

JavaScript Servers - Module 2 Page 82


ACTIVITY

While creating a new room, let the client set the room’s link
colour. It should be the same as in the activity in the previous
chapter:

The client should provide it as the “color” string property in the


request’s body. This property isn’t mandatory. Your code
should still handle requests with no “color” property provided.
You can assume that it is correct if the user provides data (you
don’t need to focus on data validation here).

After adding the following three rooms:

We should see the following:

JavaScript Servers - Module 2 Page 83

If you completed the previous exercise, after entering /new


Saving rooms permanently
We can now add new rooms, but they disappear once the server is down. While we
want to keep some rooms temporarily, we also want to add some permanently.
We’ll add a Boolean variable to our JSON request named “save”. We’ll move room
names from the array to a separate file. We’ll read the file at the beginning. If save
is false, nothing will change. If true, we will save the new name to the file.
Let’s create a “rooms.json” file with this array:
["room1", "room2"]

Now move to the “index.js” server file. We need to attach the “fs” module to work
with files:
var fs = require('fs');

Let’s read the JSON value to the room array:


let rooms = JSON.parse(fs.readFileSync('./rooms.json', 'utf-
8'));

We pass path to the JSON file as the first parameter and encoding format as the
second parameter.
Now we need to edit the code by adding new rooms. If the room of the sent name
already exists, we should return an error in response. Otherwise, if “save” is true,
we should add this to our JSON file. Regardless of the “save” value, we need to
add this to the array of the current room:
app.post('/newroom', jsonParser, (req, res) => {
const room = req.body.room;
app.get('/' + room, (req, res) => {
res.render(__dirname + '/room.ejs', {room:
room});
});
if(!rooms.includes(req.body.room)) {

JavaScript Servers - Module 2 Page 84


rooms.push(room);
if(req.body.save) {
let rooms =
JSON.parse(fs.readFileSync('./rooms.json', 'utf-8'));
const newRooms = rooms.concat([req.body.room])
fs.writeFileSync("./rooms.json",
JSON.stringify(newRooms));
}
res.send({
'room': room
});
}
else {
res.send({
'error': 'room already exist'
})
}
})

We can see that code is working correctly.


Now add “newRoom1” with “save”=true and “newRoom2” with “save”=false:

JavaScript Servers - Module 2 Page 85


We can see in the “rooms.json” file that “newRoom1” was saved, but “newRoom2”
was not. This behaviour is correct!
And now, let’s try to add newRoom1 again:

JavaScript Servers - Module 2 Page 86


ACTIVITY

Before starting this exercise, complete at least one of the


previous two.

In this exercise, you need to use the fs.writeFileSync() to save


in a file the colour information of the new rooms (the one you
added in the previous activities).

After restarting the application, we can reproduce features


implemented in the previous activities.

Answer: Click here to reveal.

JavaScript Servers - Module 2 Page 87


What did I learn in this lesson?
This lesson provided the following insights:
 Web sockets and when to use them.
 How to install and use Socket.IO.
 How to create a real-time chatroom with the use of ExpressJS and
Socket.IO.
 How to use web sockets and template engine together.
 How to handle POST requests with ExpressJS.
 How to create rooms dynamically and save them after the server resets.

References
LinkedIn. (n.d.). Node.js: Real-Time Web with Socket.IO Online Class | LinkedIn
Learning, formerly Lynda.com. [online] Available at:
https://www.linkedin.com/learning/node-js-real-time-web-with-socket-io.
socket.io. (n.d.). Introduction | Socket.IO. [online] Available at:
https://socket.io/docs/v4/.
socket.io. (n.d.). Get started | Socket.IO. [online] Available at: https://socket.io/get-
started/.
Stack Overflow. (n.d.). javascript - Using socket.io in Express 4 and express-
generator’s /bin/www. [online] Available at:
https://stackoverflow.com/questions/24609991/using-socket-io-in-express-4-and-
express-generators-bin-www.
MDN Web Docs. (2019). The WebSocket API (WebSockets). [online] Available at:
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API.
ejs.co. (n.d.). EJS -- Embedded JavaScript templates. [online] Available at:
https://ejs.co/#docs.
expressjs.com. (n.d.). Express 4.x - API Reference. [online] Available at:
https://expressjs.com/en/4x/api.html.

JavaScript Servers - Module 2 Page 88


2.2. Lesson task - Web sockets and
ExpressJS application

The task
In this lesson, we learnt how to combine features to improve the application. In this
part of the task, you need to:
Store all messages from room1 (the ones sent from the clients, not the ones about
joining/disconnecting) in the JSON file (name it messages.json) and send them to
the client whenever it joins the room.
Source code: Click here to reveal index.js.
Source code: Click here to reveal room.ejs.
Source code: Click here to reveal created message.json file (at the same level as
index.js file).

JavaScript Servers - Module 2 Page 89


2.3. Lesson - Advanced Node
Package Creation and
Authentication

Introduction

We learnt how to download and use external packages from Node Package
Manager. With these packages, we can use various functionalities without
implementing them by ourselves. In this lesson, we’ll also learn how to create and
publish our packages to contribute to the community.
To use NPM, you don’t need an account, but you’ll need one to publish your own
packages. We’ll create a package with simple functionality in this course. Later,
we’ll learn about publishing details such as versions and tags.
Finally, we’ll publish the package and learn how to update it. We can then delete it
from the registry if we want to.
This lesson will also discuss a very important issue - authentication.

JavaScript Servers - Module 2 Page 90


While creating a big application, we want users to have their own personalised
views. We can achieve this by creating accounts for each user and storing their
data independently.
We’ll learn how to create user authentication using the PassportJS library. There
are several methods of authentication. We’ll go through the official tutorial on the
PassportJS website about using a username and password to authenticate users.
However, we could also use external accounts, such as Google, Facebook, or
Microsoft accounts.
We’ll start with the external project and extend it by the login page. We’ll also learn
how to create password verification functionality and allow users to sign in, sign out
and sign up.

Learning outcomes
In this lesson, we are covering the following knowledge learning
outcome:
 The candidate has knowledge of concepts, processes, and tools of
server-based JavaScript solutions.

In this lesson, we are covering the following skill learning outcome:


 The candidate can apply vocational knowledge of server-based
JavaScript solutions to practical and theoretical solutions.

In this lesson, we are covering the following general competence


learning outcome:
 The candidate understands the ethical principles that apply when
developing, deploying and maintaining server-based JavaScript
solutions.

JavaScript Servers - Module 2 Page 91


Creating an NPM account
To create an NPM account, open this link first: https://www.npmjs.com/signup.
Provide a unique username, your e-mail address, and a password of at least 10
characters:

After that, check your email for the code and paste it into the next window:

JavaScript Servers - Module 2 Page 92


After that, you should be able to set a new password. When clicking your avatar,
you should be able to see your profile. You can change your data or link your
GitHub and Twitter accounts here:

In the Packages menu, we can see the packages we’ll publish in the next chapter.

READ

Page: Creating a new user account on the public registry by


npm Docs.

JavaScript Servers - Module 2 Page 93


Package setup
Before we publish our package, we need to create it, have an NPM account and
create a proper README.
Let’s open VS Code and create a new “npm-testing” folder. We’ll try to log in to the
NPM with the account we just created on the NPM website. Let’s type the
command in the terminal:
npm login

You’ll be able to log in with our username and password. We also need to provide
an email. Next, check your email. You should receive an email with a confirmation
code. Enter this code in the VS Code console.
After that, we can check if the login attempt succeeded by checking the current
user with the command:
npm whoami

If everything went correctly, we should get results similar to these presented below
(but with your unique username):

Next, use the command:


npm init

to create a new project. After that, you’ll see a form in the console. Fill it with the
unique name and description of the project, and put your username as the author.

JavaScript Servers - Module 2 Page 94


You fill in each form property by typing the value and clicking ENTER to move to
the next one. You should get results similar to those presented below:

Let’s create two files in this folder:


index.js:
module.exports = function addFourNumbers(a, b, c, d) {
return a + b + c + d;
}

JavaScript Servers - Module 2 Page 95


and test.js:
var assert = require('assert');
var addFourNumbers = require('./index');

assert.strictEqual(addFourNumbers(0, 1, 2, 3), 6);


assert.strictEqual(addFourNumbers(3, 2, 1, 0), 6);

console.log("Tests completed!");

In “index.js”, we export a very simple function adding four numbers.

In “test.js”, we test our function using the “assert” module. The assert.scrictEqual()
method checks whether two values are identical. We can run this test file with the
command.
node test

You should see this result:

If we change the second argument in assert from 6 to 5, the result will be:

JavaScript Servers - Module 2 Page 96


As you can see, the two arguments are no longer the same.
We want these tests to run with NPM, not Node. We can achieve this by adding the
node test

command in “scripts” object in “package.json”:


{
"name": "add-four-numbers",
"version": "1.0.0",
"description": "Function adding four numbers",
"main": "index.js",
"scripts": {
"test": "node test.js"
},
"author": "learnwithme",
"license": "ISC"
}

Now this command works properly in the following results:

JavaScript Servers - Module 2 Page 97


Before we can publish the package, we should create a proper README.md file. It
should include general information about the package and directions for installing
and configuring. It should also have instructions on how to use these packages and
usage examples. This README file will be visible on the package’s main page.
You can check example of such README.md file below:
# Add four numbers

This module helps you to add four numbers.

## Install

```
npm install add-four-numbers
```

## USAGE
**`addFourNumbers(a, b, c, d)`**

```
// Load library
var addFourNumbers = require('add-four-numbers');

// Calculate 0 + 1 + 2 + 3
console.log(addFourNumbers(0, 1, 2, 3)); // => 6
```
## Test

```
npm test
```
License ISC

JavaScript Servers - Module 2 Page 98


Our package is finally ready to be published!

WATCH

Video: Requirements for publishing (1m 48s) by Emmanuel


Henri on LinkedIn Learning.

Versions and tags


The packages we used had versions in the dependencies. Three numbers
describe these versions:
1. Major releases – usually full new versions of new software.
2. Minor releases – adding new functionalities to the major releases.
3. Patch releases – small changes, mainly bugfixes.

There are some characters before versions too:


 Caret before version – major release with all the most recent minor
releases and patches will be installed.
 Tilde – minor release with all the most recent patches will be installed.

JavaScript Servers - Module 2 Page 99


If there is no character before the version, the specified version will be installed.
On the other hand, dist-tags make the release more readable. For example, we
might use words like alpha, beta, stable, etc., to tell people about the app’s current
state. We can add them either during or after publishing.

READ

1. Page: About semantic versioning by npm Docs.


2. Page: Adding dist-tags to packages by npm Docs.

WATCH

1. Video: Basics of semantic versioning (2m 1s) by


Emmanuel Henri on LinkedIn Learning.
2. Video: Introduction to dist-tags (1m 30s) by Emmanuel
Henri on LinkedIn Learning.

JavaScript Servers - Module 2 Page 100


Publishing package
With a free NPM account, we can only publish public packages – available for
everyone. However, with a paid account, we could publish private packages – only
for our organisation or us. In this section, we’ll only consider public publishing.
To publish a package, we should use the npm publish command. Package after
publishing is publicly available at www.npmjs.com. Moreover, anyone can install it
with NPM.
While publishing or changing our package, we can provide a dist-tag. Dist-tags are
more intuitive. It’s much more helpful to see which version is stable or when the
application went out of beta rather than seeing only the version number. To add a
dist-tag to the package, add
--tag <tagName>

to the publishing command.


Let’s add the beta dist-tag to tell potential users that it’s the beta version – the
version that hasn’t reached production standards yet contains experimental
features and possible bugs.
npm publish --tag beta

Make sure that your package name is unique. If it isn’t, you should see an error. To
fix it, you need to change that package name in the package.json file.
If everything goes according to plan, we should get an output similar to this:

JavaScript Servers - Module 2 Page 101


Now we can check our account on the NPM website. We should be able to see the
new package:

JavaScript Servers - Module 2 Page 102


We can now try to install it in any other NPM project (in our case, this project is
located in the folder named “node”. Create an empty folder and run the npm init
command to get such a project) with the following command:
npm install add-four-numbers

JavaScript Servers - Module 2 Page 103


We should also see that the project got imported into node modules:

READ

Page: Creating and publishing unscoped public packages by


npm Docs.

WATCH

Video: Publish your package (3m 14s) by Emmanuel Henri on


LinkedIn Learning.

JavaScript Servers - Module 2 Page 104


Updating packages
In this section, we’ll learn how to update and delete our packages. Let’s open our
package folder in the VSCode console and add an external dependency, for
example – socket.io:

Adding a new dependency is a tiny update and can be classified as either a minor
release or a patch. Let’s open the “package.json” file and change the version to
1.0.1. (classify this change as a patch):
{
"name": "add-four-numbers",
"version": "1.0.1",
"description": "Function adding four numbers",
"main": "index.js",
"scripts": {
"test": "node test.js"
},
"author": "learnwithme",
"license": "ISC",
"dependencies": {
"socketio": "^1.0.0"
}
}

JavaScript Servers - Module 2 Page 105


All we have to do is publish the package in the same way again:
npm publish --tag socketio

JavaScript Servers - Module 2 Page 106


Let’s open the project we installed a new package on before. In our “package-
lock.json file”, we should see the following:
"node_modules/add-four-numbers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/add-four-
numbers/-/add-four-numbers-1.0.0.tgz",
"integrity": "sha512-
GqDZIfcbHA5STXnB3kBPLdDHwFj469Dv7pvV1sIHf+w86Zmv2tvDJI8Bw2dJL
Qt/JzQq3LcEzX121aoIa+ZKeQ=="
},

Let’s open the VSCode console, ensuring we are in the folder where we installed
the package in the previous chapter and run the command:
npm install

It should have no result, as, by default, our module was installed as version 1.0.0
without any characters before. Let’s change the version to: “~1.0.0” and rerun the
command:

JavaScript Servers - Module 2 Page 107


The package was successfully updated!
Now let’s see how to unpublish the package:
Go to the link: https://www.npmjs.com/package/your-package-name/access and
scroll down (replace “your-package-name” with the name of your package). We
should see these options:

JavaScript Servers - Module 2 Page 108


The other way to unpublish the package is by using the command:
npm unpublish <package-name>@<version>

We can use it to unpublish a single version or completely unpublish the entire


package. To unpublish the entire package, we need to add a flag -f. Let’s unpublish
this package with the command:
npm unpublish add-four-numbers -f

We should get the following result:

JavaScript Servers - Module 2 Page 109


When we try to install the package again, we get this error:

We see this error because our add-four-numbers package is already unpublished –


we can no longer download and install it.

READ

1. Page: Updating your published package version number


by npm Docs.
2. Page: Unpublishing packages from the registry by npm
Docs.

WATCH

Video: Update your package (3m 3s) by Emmanuel Henri on


LinkedIn Learning.

JavaScript Servers - Module 2 Page 110


Authentication and PassportJS
We create user authentication to verify that our application’s users are who
they claim they are. There are three main authentication strategy classes:
 The knowledge test – verifies users’ identity based on whether or not
they know something - for example, their username and password.
 The ownership test – verifies users’ identity based on whether or not
they have something – for example, a verification code sent to their
email or mobile phone.
 The biological test - verifies users’ identity based on hard-to-fake users’
physical characteristics – for example, facial recognition or fingerprint
readers.

All of these tests have their disadvantages. Weak passwords can be guessed, and
phones can be stolen. Because of that, in many applications, two-factor
authentication is used. It combines more than one authentication method, usually
knowledge and ownership tests.
In this course, we’ll use PassportJS – authentication middleware for NodeJS. It
provides us with an authenticate method. Then we’ll choose one from over 500+
authentication strategies, using username and password, Twitter, Facebook, or
more. In this section, we’ll learn about username and password authentication.
Once the user is authenticated, we store their data in the session, so they are still
logged in even after refreshing the website or moving to another endpoint. The
entire authentication process is presented in the picture below:

Passport provides an authenticate function. This function uses one of over 500
strategies to authenticate the user. Strategies are pluggable authentication
modules installed separately. Once the user is authenticated, their data is stored in
the session and used for every request. The Serialize function determines which

JavaScript Servers - Module 2 Page 111


data of the user should be stored in the session. It always saves the user id. The
Deserialize function uses this id to retrieve user data.

READ

Documentation: PassportJS by PassportJS.

WATCH

1. Video: What is user authentication? (6m 27s) by Shaun


Wassell on LinkedIn Learning.
2. Video: Using Passport.js for authentication (1m 55s) by
Daniel Khan on LinkedIn Learning.

Project setup
In this lesson, we’ll create authentication for a simple TODO list project. We’ll go
through the official PassportJS tutorial. We can clone a starting project from the
repository. It uses a database (SQLite). As we haven’t learned about databases
yet, we’ll treat this functionality as the BlackBox – we won’t focus on understanding
database code but its effects.
The project uses Embedded JavaScript (EJS) as the template engine. It has
already implemented the TODO list logic. Whenever we add an item, it is added to
the database. However, the user needs to be signed in first to use it. We’ll also use
this logic as the BlackBox; the primary process we’ll focus on is creating user
authentication.
The final version of the app is available at: https://github.com/passport/todos-
express-password, so we can see how it should work.
The first user needs to sign in:

JavaScript Servers - Module 2 Page 112


They can then sign in or sign up if they don’t have an account yet:

After that, they can use the TODO list:

Let’s start the project setup. Create a folder for the project and open it in the
VSCode terminal. Type the command:
git clone https://github.com/passport/todos-express-
starter.git username-password-tutorial

JavaScript Servers - Module 2 Page 113


and enter the username-password-tutorial folder with the command:
cd username-password-tutorial

Notice that all views and styles we will be used in further chapters are already
implemented:

We can see that the project uses NPM – there is a “package.json” file. There is a
start command in this file, which starts the app. Let’s install all dependencies and
run the application:
npm install
npm start

JavaScript Servers - Module 2 Page 114


We can see the application is running even though the login endpoint isn’t
implemented yet:

We’ll fix this in the next chapter.

READ

Tutorial: Introduction by PassportJS.

JavaScript Servers - Module 2 Page 115


Login prompt
In this chapter, we’ll create a page where users can sign into the application. Let’s
create a new file in the routes folder containing authentication-related routes and
name it “auth.js”. Fill it with the following code:
var express = require('express');

var router = express.Router();

router.get('/login', function(req, res, next) {


res.render('login');
});

module.exports = router;

It’s a standard ExpressJS endpoint, which renders “login.ejs” view on /login path.
Let’s add this router file in “app.js”:
/* code before */
var indexRouter = require('./routes/index');
var authRouter = require('./routes/auth');

var app = express();

app.locals.pluralize = require('pluralize');

// view engine setup


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);

JavaScript Servers - Module 2 Page 116


app.use('/', authRouter);
/* code after */

We can now run the application.

We can see the sign-in view, but the user can’t give us any data yet. Let’s create a
form for that and add it to the “login.ejs” file:
login.ejs:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,
initial-scale=1">
<title>Express • TodoMVC</title>
<link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="/css/index.css">
<link rel="stylesheet" href="/css/login.css">
</head>
<body>
<section class="prompt">
<h3>todos</h3>
<h1>Sign in</h1>
<form action="/login/password" method="post">
<section>
<label for="username">Username</label>
<input id="username" name="username"
type="text" autocomplete="username" required autofocus>
</section>
<section>
<label for="current-

JavaScript Servers - Module 2 Page 117


password">Password</label>
<input id="current-password"
name="password" type="password" autocomplete="current-
password" required>
</section>
<button type="submit">Sign in</button>
</form>
<hr>
<p class="help">Don't have an account? <a
href="/signup">Sign up</a></p>
</section>
<footer class="info">
<p>Created by <a
href="https://www.jaredhanson.me">Jared Hanson</a></p>
<p>Part of <a
href="https://todomvc.com">TodoMVC</a></p>
<p>Authentication powered by <a
href="https://www.passportjs.org">Passport</a></p>
</footer>
</body>
</html>

We added a simple form with labels and inputs and a button to submit it.

After submission, a POST request to /login/password path is sent. We’ll create this
endpoint in the next chapter.

JavaScript Servers - Module 2 Page 118


READ

Tutorial: Login Prompt by PassportJS.

Verify password
In this chapter, we’ll finally use PassportJS and its local strategy. Let’s install them
first with NPM:
npm install passport
npm install passport-local

and include them in “auth.js”. We’ll need our database and built-in encryption
module as well:
var express = require('express');
var passport = require('passport');
var LocalStrategy = require('passport-local');
var crypto = require('crypto');
var db = require('../db');

We’ll use a passport.use() method to choose the authentication strategy. The local
strategy takes a verification function as the parameter. This function takes a
username, password, and callback function. Let’s paste this code after all
requirements:

JavaScript Servers - Module 2 Page 119


passport.use(new LocalStrategy(
function verify(username, password, callback) {
db.get('SELECT * FROM users WHERE username = ?', [
username ], function(err, row) {
if (err) { return callback(err); }
if (!row) { return callback(null, false, { message:
'Incorrect username or password.' }); }

crypto.pbkdf2(password, row.salt, 310000, 32, 'sha256',


function(err, hashedPassword) {
if (err) { return callback(err); }
if (!crypto.timingSafeEqual(row.hashed_password,
hashedPassword)) {
return callback(null, false, { message: 'Incorrect
username or password.' });
}
return callback(null, row);
});
});
})
);

The verify() function first uses the query to find all records of the provided
username in the database. We’ll learn more about databases and queries in the
next course. For now, all we need to know is that it finds all rows where the
username is the same as provided.

While we can send a username and keep it in the database as plain text, we
shouldn’t do this with a password. We encrypt the password with the sha256
algorithm (one of the most popular algorithms for authentication and data
encryption ) and a built-in encryption module.

Encryption makes the knowledge test much safer. If an encrypted password is the
same as in the database, for now, we move to the next step by returning the row in
the callback. Otherwise, we send an error in callback instead.
After the Passport setup is done, let’s use it in the endpoint. Let’s create a
/login/password handler in the auth.js router file:

JavaScript Servers - Module 2 Page 120


router.post('/login/password', passport.authenticate('local',
{
successRedirect: '/',
failureRedirect: '/login'
}));

We use the passport.authenticate() method to add authentication for the endpoint.


If we get an error in the callback, we redirect the user to the /login endpoint again. If
everything goes correctly, we redirect them to the home page.
We can’t add new users yet, as this would require changes in our database.
Databases aren’t the focus of this lesson. They will be covered in detail in the next
course.
Fortunately, there’s one user created manually during the database creation:

Let’s start the application and use these credentials to try to sign into the login view
(http://localhost:3000/login) :

However, after this trial, we should see this result:

JavaScript Servers - Module 2 Page 121


This happens because we don’t have the proper session setup yet. Well learn what
a session is and how to fix it in the next chapter.

READ

Tutorial: Verify Password by PassportJS.

Session
Sessions are a way to store users’ data temporarily on the server side. After users
log in, we give them an access token that the browser remembers. A session
usually gets deleted when the user exits the browser. This way, after each refresh
or attempt to access another endpoint, the user doesn’t need to reauthenticate
each time.
Let’s add session support to our application. Start by installing an express session
module and a module for session management for our database with NPM
commands:
npm install express-session
npm install connect-sqlite3

Open the “app.js” file and add additional dependencies.


var passport = require('passport');
var session = require('express-session');
var SQLiteStore = require('connect-sqlite3')(session);

We use the connect-sqlite3 package to create a place to keep data about our
current active sessions. This package stores this information using the database.

JavaScript Servers - Module 2 Page 122


We’ll learn how databases work in detail in the next course. For now, please treat
this as a place where we store information about each session’s id, expiration date
and other user details.
We can now add session support above adding routers to the app:
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store: new SQLiteStore({ db: 'sessions.db', dir: './var/db'
})
}));
app.use(passport.authenticate('session'));
app.use('/', indexRouter);
app.use('/', authRouter);

Using just created SQLiteStore, this code creates a new temporary database to
keep sessions. This database will be named “sessions.db” and placed in the
_current_path/var/db directory. As the secret, we can pass any string or array of
strings. It is used to sign a session cookie.
Now we need to implement serialization and deserialization of the user to the
session. PassportJS has serializeUser() and deserializeUser() methods to do that.
The Serialization method saves users’ data to the session, while the Deserialization
method retrieves this data using the user id.
Let’s use them in the “auth.js” file:
passport.serializeUser(function(user, callback) {
process.nextTick(function() {
callback(null, { id: user.id, username: user.username });
});
});

passport.deserializeUser(function(user, callback) {
process.nextTick(function() {
return callback(null, user);
});
});

JavaScript Servers - Module 2 Page 123


We can restart the application and log in as Alice again. It should work correctly
now:

Right now, we should have a working application. However, we still miss the
functionalities of switching users and signing up new ones.
We can implement signing out from the app easily with the Express
request.logout() method. It finishes the session and takes the callback function as
the parameter. Let’s redirect the user to the home page in that callback:
auth.js:
router.post('/logout', function(req, res, next) {
req.logout(function(err) {
if (err) { return next(err); }
res.redirect('/');
});
});

We see that after adding this endpoint, we can sign out (it was already linked in an
“index.ejs” file).
Adding new users is more complex.
Let’s create /signup endpoint in “auth.js”:
router.get('/signup', function(req, res, next) {
res.render('signup');
});

In “signup.ejs”, add analogous to the one added in the “login.ejs”:

JavaScript Servers - Module 2 Page 124


<h1>Sign up</h1>
<form action="/signup" method="post">
<section>
<label for="username">Username</label>
<input id="username" name="username"
type="text" autocomplete="username" required>
</section>
<section>
<label for="new-
password">Password</label>
<input id="new-password" name="password"
type="password" autocomplete="new-password" required>
</section>
<button type="submit">Sign up</button>
</form>

Now we have the front end ready. We still need to implement the logic of saving
new users to the database. As this code is database related, we’ll go through it and
explain each part without analysing the database query part:
auth.js:
router.post('/signup', function(req, res, next) {
var salt = crypto.randomBytes(16);
crypto.pbkdf2(req.body.password, salt, 310000, 32,
'sha256', function(err, hashedPassword) {
if (err) { return next(err); }
db.run('INSERT INTO users (username, hashed_password,
salt) VALUES (?, ?, ?)', [
req.body.username,
hashedPassword,
salt
], function(err) {
if (err) { return next(err); }
var user = {
id: this.lastID,
username: req.body.username
};
req.login(user, function(err) {
if (err) { return next(err); }

JavaScript Servers - Module 2 Page 125


res.redirect('/');
});
});
});
});

As we want to encrypt the credentials before saving, we generate the salt first and
pass it to encrypting function pbkdf2(). Salt is a randomly generated byte sequence
used in encryption because people tend to choose the same passwords, and not at
all randomly. Many used passwords are actual, short words to make them easy to
remember, but this could also enable an attack. We apply salt to the password to
make the password hash output unique.
Next, we insert the user’s username, encrypted password, and salt into the
database. If everything goes correctly, we log in to the new user and redirect them
to the home page.
We can now run the application and check that all functionalities are complete.
If we try to add an already existing user, we should see the error, as the database
is preventing us from doing that:

READ

1. Tutorial: Establish Session by PassportJS.


2. Tutorial: Log Out by PassportJS.
3. Tutorial: Sign Up by PassportJS.

JavaScript Servers - Module 2 Page 126


WATCH

1. Video: Cookies and sessions (2m 55s) by Kevin


Skoglund on LinkedIn Learning.
2. Video: Implementing local authentication with
Passport.js (16m 18s) by Daniel Khan on LinkedIn
Learning.

What did I learn in this lesson?


This lesson provided the following insights:
 How to create and publish a new package.
 How versions and tags work.
 How to update and delete a package.
 Authentication and how to use PassportJS to create it.
 How to create a login page with password verification.
 How to make users sign in, sign out and sign up.

References
docs.npmjs.com. (n.d.). npm Documentation. [online] Available at:
https://docs.npmjs.com/.
LinkedIn. (n.d.). Advanced npm Online Class | LinkedIn Learning, formerly
Lynda.com. [online] Available at: https://www.linkedin.com/learning/advanced-npm.
LinkedIn. (n.d.). Web Security: User Authentication and Access Control Online
Class | LinkedIn Learning, formerly Lynda.com. [online] Available at:
https://www.linkedin.com/learning/web-security-user-authentication-and-access-
control.

JavaScript Servers - Module 2 Page 127


LinkedIn. (n.d.). Node: Authentication Online Class | LinkedIn Learning, formerly
Lynda.com. [online] Available at: https://www.linkedin.com/learning/node-
authentication.
GitHub. (2022). todos-express-password. [online] Available at:
https://github.com/passport/todos-express-password.

2.3. Lesson task - Advanced Node


Package Creation and
Authentication

The task
Part 1

In this lesson, we learnt about publishing packages with NPM.


In this part of the task, you need to create a module that checks whether the
lengths of two strings have the same parity. Create and export function taking two
strings and returning Boolean: True if the length of both strings is even/odd, False if
parity is different.
Create a unique name for your package and publish it.
Create a README.md file and update the package with the 1.1.0 version with the
tag add_README.
Install the package in the other project and import this functionality.

After testing the solution and ensuring it works correctly, unpublish the package.

Part 2

In this lesson, we also learnt about authentication with PassportJS. We went over
the tutorial about authentication with username and password. In this task, you
need to discover other ways to authenticate by finishing the guide on any other
PassportJS authentication strategy, for example, authentication with a Google
account.

JavaScript Servers - Module 2 Page 128


READ

1. Tutorial: Google by Passport.js to learn more about


Google authentication.
2. Tutorial Documentation by Passport.js to help you
understand Passport and use it in your applications.

INFO

 Code at Configure Strategy (the one setting up the


Google Strategy) uses databases. Database setup is
already done in the starting repository. In this place, we
use the database to store Google credentials. If the user
logs in with their google account for the first time, we
add this data to the database.
 Code at Maintain State uses database code to set up the
session store with the connect-sqlite3 package. This is
the exact code we used in this lesson, and it works in
the same way.

JavaScript Servers - Module 2 Page 129


2.4. Lesson - Asynchronous NodeJS,
streams and security

Introduction

We have already learned the basics of asynchronous coding in JavaScript, such as


callbacks and promises. In this lesson, we’ll extend this knowledge and learn how
to apply it to NodeJS.
At first, we’ll revise some knowledge of callbacks and promises. We’ll also learn
about the differences between sequential, parallel, and concurrent execution of the
processes.
We’ll introduce streams and learn about different stream types, as well as
advanced parts of streams, such as backpressure and transformations.
In the second part of this lesson, we’ll address issues related to application
security. Many web applications are not secure or can be easily hacked. Being
attacked successfully from the outside is a severe threat and might lead to extreme
costs for the client. In this lesson, we will learn the basics of security, which will
help us prevent threats.

JavaScript Servers - Module 2 Page 130


Open Web Application Security Project (OWASP) is a non-profit organisation that
works to improve security standards. This lesson will teach us about security and
go through the OWASP security standards.
We’ll focus on securing our packages, data, and server by looking at the most
popular attack types and security tools to prevent them.

Learning outcomes
In this lesson, we are covering the following knowledge learning
outcome:
 The candidate has knowledge of concepts, processes, and tools of
server-based JavaScript solutions.

In this lesson, we are covering the following skill learning outcomes:


 The candidate can apply vocational knowledge of server-based
JavaScript solutions to practical and theoretical solutions.
 The candidate masters the use of a server framework for building
node-based web servers.

In this lesson, we are covering the following general competence


learning outcome:
 The candidate understands the ethical principles that apply when
developing, deploying and maintaining server-based JavaScript
solutions.

Callbacks and promises


Create a playground.js file. There, we can test code examples about asynchronous
JavaScript and streams.
NodeJS is asynchronous and has only one thread. Thread is the smallest
sequence of programmed instructions that can be managed independently by an
operating system.

JavaScript Servers - Module 2 Page 131


We can use callbacks and promises to improve its efficiency. The callback is a
block of instruction wrapped in a function to be called after the asynchronous
operation completes. In NodeJS, we can tell that function what to execute in the
next loop with the process.nextTick() method. Let’s look at an example.
function addOne(number, done) {
done(number + 1);
}

addOne(5, (increased) => console.log(increased))

console.log('end')

is synchronous. We wait until each operation finishes and then move to the next
lines of our code. It should give the following results:

To make it asynchronous, we need to process the done() method in the next tick:
function addOne(number, done) {
process.nextTick(() => {
done(number + 1);
});
}

addOne(5, (increased) => console.log(increased))

console.log('end')

It should give the following results:

JavaScript Servers - Module 2 Page 132


Another way to introduce asynchronous code is to use setTimeout() or setInterval()
methods. We should already know them, and they take the callback function and
number of seconds as the parameters. The callback function runs after the
specified time.
Promises are used to wait until our operation completes and return the result with
the then() method. Let’s look at an example.
var delay = (seconds) => new Promise((resolves, rejects) => {
setTimeout(() => {
resolves('the long delay has ended')
}, seconds);
});

delay(1)
.then(console.log)
.then(() => 42)
.then((number) => console.log(`Hello world: ${number}`))

console.log('end first tick');

We created the delay() method, which takes several seconds as the argument. The
method uses promises to return the text after the delay.
We can pass the function to execute after the promise resolves with the then()
method. We can pass the value returned from the function to pass it further in the
next then() methods. We do this in this code section:
delay(1)
.then(console.log)
.then(() => 42)
.then((number) => console.log(`Hello world: ${number}`))

First, then() doesn’t return anything. Second, then() returns a number, and we use
it in the third then().
We get the expected results:

JavaScript Servers - Module 2 Page 133


While using promises, we can throw errors with the reject() function. These errors
can be handled later with the catch() method. We use the catch() method after
invoking the function.

Let’s look at an example:


var delay = (seconds) => new Promise((resolves, rejects) => {

if (seconds > 3) {
rejects(new Error(`${seconds} is too long!`))
}

setTimeout(() => {
resolves('the long delay has ended')
}, seconds);
});

delay(1)
.then(console.log)
.then(() => 42)
.then((number) => console.log(`Hello world: ${number}`))
.catch((error) => console.log(`error: ${error.message}`));

console.log('end first tick');

When we run the delay function with 5 as the parameter, we get the error:

JavaScript Servers - Module 2 Page 134


Callbacks are functions passed as arguments into other functions executed after
the asynchronous operation finishes.
Promises are placeholder objects for asynchronous data. As soon as their state
changes from pending to resolved, the .then() method can be called to operate on
the data.
NodeJS functionalities usually use callbacks. However, we can easily change them
into promises with the promisify() function from the util module. Let’s look at an
example:
var fs = require('fs');
var { promisify } = require('util');

var writeFile = promisify(fs.writeFile);

writeFile('sample.txt', 'This is a sample')


.then(() => console.log('file successfully created'))
.catch((error) => console.log('error creating file'));

The file system writeFile() method is based on callbacks. With the promisify()
method, we can use this method as a promise.

WATCH

1. Video: Callback pattern (8m) by Alex Banks on LinkedIn


Learning.
2. Video: Resolving promises (5m 40s) by Alex Banks on
LinkedIn Learning.
3. Video: Rejecting promises (2m 58s) by Alex Banks on
LinkedIn Learning.
4. Video: The promisify function (5m 18s) by Alex Banks
on LinkedIn Learning.

JavaScript Servers - Module 2 Page 135


Sequential and parallel execution
All the examples mentioned in the first chapter are about sequential execution.
Even if things execute asynchronously and we use the process.nextTick() method
to move the execution of the function to the other phase, functions still execute in
some order, which will always be the same.
Parallel execution is about running multiple tasks at the same time. We can use
Promise.all() or Promise.race() to implement it. These methods run an array of
promises:
 Promise.all() waits until all tasks end and then moves to the resolve part.
 Promise.race() waits until the first task finishes.

Here is an example:
var delay = (seconds, number) => new Promise((resolves) => {
setTimeout(() => {
resolves(number)
console.log(number)
}, seconds * 1000);
});

Promise.all([
delay(10, 1),
delay(6, 2),
delay(5, 3),
delay(6, 4)
]).then(console.log);

We create an array of four tasks and simulate their execution with a delay function.
Running this program will give the following results:

JavaScript Servers - Module 2 Page 136


The third task takes the least time, so its log is displayed first. The console.log
method used in the then() method displays the entire array because it waits until all
tasks finish.
Let’s change Promise.all to Promise.race and run the program again.
We should get this result:

The then() method is executed right after getting the first result (from the third task)
as the Promise.race method waits only for the first task to be completed.
Executing tasks sequentially or in parallel has pros and cons. While executing in
parallel, we can get tasks completed faster. However, when some of the tasks are
large, we might not have enough resources to complete them.
We can combine both of these options and create a concurrent task queue. It runs
tasks in parallel, but only a specified number can run simultaneously.
Let’s see an example of a class implementation.
class PromiseQueue {

constructor(promises=[], concurrentCount=1) {
this.concurrent = concurrentCount;
this.total = promises.length;
this.todo = promises;
this.running = [];
this.complete = [];
}

get runAnother() {
return (this.running.length < this.concurrent) &&
this.todo.length;
}

run() {
while (this.runAnother) {
var promise = this.todo.shift();

JavaScript Servers - Module 2 Page 137


promise.then(() => {
this.complete.push(this.running.shift());
this.run();
})
this.running.push(promise);
}
}
}

At first, we initialise our to-do array with all promises in a promises[] array. The
runAnother() method checks if there are still tasks to complete and if we have free
spots in the “running” array. If we do, we take the first promise from the to-do array
and place it in the running array. After the promise resolves, we push it to the
complete array and, as we have a new spot left, check whether some tasks are still
waiting for execution.

WATCH

1. Video: Sequential execution (7m 6s) by Alex Banks on


LinkedIn Learning.
2. Video: Sequential execution with async/await (7m) by
Alex Banks on LinkedIn Learning.
3. Video: Parallel execution (4m 6s) by Alex Banks on
LinkedIn Learning.
4. Video: Concurrent tasks (6m 3s) by Alex Banks on
LinkedIn Learning.
5. Video: Logging concurrent tasks (3m 3s) by Alex Banks
on LinkedIn Learning.

JavaScript Servers - Module 2 Page 138


Introduction to streams
Streams are widely used in NodeJS tools to manage file transport. The standard
way of sending the data can result in huge memory usage, for example:
fs.readFile(file, (error, data) => {
res.writeHeader(200, { 'Content-Type': 'video/mp4'
});
res.end(data);
})

With streams, instead of sending all the data at the same time, we estimate how
much we can process at the time and send data part by part. We can create
streams with a standard file system module:
res.writeHeader(200, { 'Content-Type': 'video/mp4' });
fs.createReadStream(file)
.pipe(res)

There are many types of streams. In this chapter, we will focus on readable and
writable ones.
We need to include a stream module and create a class extending Readable to
implement a readable stream. Let’s create a class for reading arrays:
const { Readable } = require('stream');

const peaks = [
"Tallac",
"Ralston",
"Rubicon",
"Twin Peaks",
"Castle Peak",
"Rose",
"Freel Peak"
];

class StreamFromArray extends Readable {

JavaScript Servers - Module 2 Page 139


constructor(array) {
super({ objectMode: true });
this.array = array;
this.index = 0;
}

_read() {
if (this.index <= this.array.length) {
const chunk = {
data: this.array[this.index],
index: this.index
};
this.push(chunk);
this.index += 1;
} else {
this.push(null);
}
}

const peakStream = new StreamFromArray(peaks);

peakStream.on('data', (chunk) => console.log(chunk));

peakStream.on('end', () => console.log('done!'));

We use streams by specifying events on which we need to do something. In this


example, on data. We are showing it in the console and on the stream end. We
show info about the end. This code, after running, gives the results as follows:

JavaScript Servers - Module 2 Page 140


We can pause and resume streams with the stream.pause() and stream.resume()
methods.
Writable streams can be used to capture data from the readable streams and do
something with it. We’ll see them in an example of creating a copy of the video file.
You need to download any video (for example, the Circle Cut Animation Video),
rename it to “video.mp4”, and place it in the folder we’re working in.
Let’s look at an example to create a copy with the name “copy.mp4”:
const { createReadStream, createWriteStream } =
require('fs');

const readStream = createReadStream('./video.mp4');


const writeStream = createWriteStream('./copy.mp4');

readStream.on('data', (chunk) => {


writeStream.write(chunk);
});

readStream.on('error', (error) => {


console.log('an error occurred', error.message);
});

readStream.on('end', () => {
writeStream.end();
});

writeStream.on('close', () => {
process.stdout.write('file copied\n');
})

JavaScript Servers - Module 2 Page 141


WATCH

1. Video: Why streams? (5m 41s) by Alex Banks on


LinkedIn Learning.
2. Video: Readable streams (6m 32s) by Alex Banks on
LinkedIn Learning.
3. Video: Using readable streams (7m 1s) by Alex Banks on
LinkedIn Learning.
4. Video: Writable streams (3m 32s) by Alex Banks on
LinkedIn Learning.

Advanced streams
In this chapter, we will overview advanced knowledge of NodeJS streams.
Backpressure is when a readable stream sends data too fast for the writable
stream to handle. We can solve this by simply stopping the readable stream until
the writable stream finishes its work. This is illustrated below.

We shouldn’t keep sending new data when the destination cannot process more
data. It would cause data loss (image on the left). We should wait until the data is
processed and then continue sending our data (image on the right).

JavaScript Servers - Module 2 Page 142


When the write stream is emptied, the drain event runs. Let’s see how to improve
our previous example and how backpressure is handled:
const { createReadStream, createWriteStream } =
require('fs');

const readStream = createReadStream('./video.mp4');


const writeStream = createWriteStream('./copy.mp4', {
//highWaterMark: 1628920128
});

readStream.on('data', (chunk) => {


const result = writeStream.write(chunk);
if (!result) {
console.log('backpressure')
readStream.pause();
}

JavaScript Servers - Module 2 Page 143


});

readStream.on('error', (error) => {


console.log('an error occurred', error.message);
});

readStream.on('end', () => {
writeStream.end();
});

writeStream.on('drain', () => {
console.log('drained');
readStream.resume();
})

writeStream.on('close', () => {
process.stdout.write('file copied\n');
})

We can set a high watermark for the write stream. It specifies the maximum amount
of data potentially processed. If it’s bigger, more memory is used, but backpressure
happens less often.
The code we have used so far takes many lines. Fortunately, there is a pipe()
method. This method connects a writable stream to the readable stream while
handling all anomalies, including backpressure, in its implementation. We can use it
to pipe a write to the read stream in only one line.

Let’s look at the previous example but using the pipe() method instead:
const { createReadStream, createWriteStream } =
require('fs');

const readStream = createReadStream('./video.mp4');


const writeStream = createWriteStream('./copy.mp4');

readStream.pipe(writeStream).on('error', console.error)

JavaScript Servers - Module 2 Page 144


The Duplex stream is readable and writable at the same time. We can use it to
process data before it comes to a writable stream. Let’s look at an example using
the PassThrough stream (one of the streams extending Duplex) to log data being
processed:
const { PassThrough } = require('stream');
const { createReadStream, createWriteStream } =
require('fs');

const readStream = createReadStream('./video.mp4');


const writeStream = createWriteStream('./copy.mp4');

const report = new PassThrough();

var total = 0;
report.on('data', (chunk) => {
total += chunk.length;
console.log('bytes: ', total);
})

readStream
.pipe(report)
.pipe(writeStream);

We can see the result before the data is saved. Several bytes are already sent to
the write stream in the console:

JavaScript Servers - Module 2 Page 145


Transform streams are a special kind of Duplex stream - instead of just passing the
data, they can modify it. The already-mentioned PassThrough stream is an
example of the transform stream.
We can implement our own Transform streams. To do this, we need to implement a
class extending Transform. It has to implement the _transform() method. It can also
implement the _flush() method, as this method will fire after the readable stream
sends all the data.
Let’s see this in an example:
const { Transform } = require('stream');
const { createReadStream, createWriteStream } =
require('fs');

const readStream = createReadStream('./sample.txt');


class ReplaceText extends Transform {

constructor(char) {
super();
this.replaceChar = char;
}

_transform(chunk, encoding, callback) {


const transformChunk = chunk.toString()

JavaScript Servers - Module 2 Page 146


.replace(/[a-z]|[A-Z]|[0-9]/g, this.replaceChar);
this.push(transformChunk)
callback();
}

_flush(callback) {
this.push('more stuff is being passed...');
callback();
}

var xStream = new ReplaceText('X');

readStream
.pipe(xStream)
.pipe(process.stdout);

We created a stream replacing all letters and numbers with a specified character.
Let’s create a “sample.txt” file in the current directory:
Hello world!

It’s me!

And run the program:

We can see that all letters got changed beside one line, which was added by the
_flush() method at the very end.

JavaScript Servers - Module 2 Page 147


WATCH

1. Video: Backpressure (4m 11s) by Alex Banks on


LinkedIn Learning.
2. Video: Piping streams (3m 44s) by Alex Banks on
LinkedIn Learning.
3. Video: Duplex streams (6m 17s) by Alex Banks on
LinkedIn Learning.
4. Video: Transform streams (5m 46s) by Alex Banks on
LinkedIn Learning.

Security overview
Open Web Application Security Project (OWASP) is the leading non-profit
organisation working on security standards. Once every few years, they release
reports of the Top 10 most popular threats, containing information about them and
how to prevent them.
On their official website, we can see a list of attacks, their code examples, and
details on how to deny them.
Although their work is created for all languages, we are mainly interested in
JavaScript/NodeJS security. Fortunately, there is a website consolidating
information from the OWASP page using NodeJS technology. Fortunately, there is
a project that teaches how OWASP Top 10 security risks apply to web applications
developed using Node.js, and how to effectively address these issues:
https://github.com/OWASP/NodeGoat.
The most common attack types are:
 Cross-site scripting – inserting JavaScript code as the form data,
executed without the developer’s expectation. We can prevent this with
data validation on both the client and the server.
 Denial of service – trying to overload the server with large data, an
unexpected number of requests, or creating an infinite loop. We can

JavaScript Servers - Module 2 Page 148


prevent this with input and form validation and mechanisms preventing
loop creation.
 Server-side injection – the attacker uses functions like eval(),
setTimeout(), or setInterval() to process malicious code. We can prevent
it by not using these methods to parse the input.

READ

Page: eval() by MDN Web Docs.

WATCH

1. Video: Introduction to OWASP and other sources (2m


49s) by Emmanuel Henri on LinkedIn Learning.
2. Video: OWASP top 10 in Node.js (2m 22s) by Emmanuel
Henri on LinkedIn Learning.
3. Video: Overview of cross-site scripting (2m 21s) by
Emmanuel Henri on LinkedIn Learning.
4. Video: Overview of denial of service (1m 13s) by
Emmanuel Henri on LinkedIn Learning.
5. Video: Overview of server-side injection (1m 15s) by
Emmanuel Henri on LinkedIn Learning.

Packages security
In NodeJS applications, we usually use a lot of external dependencies. We should
remember to keep them up-to-date. Outdated versions can have vulnerabilities that
can expose us to attacks.

JavaScript Servers - Module 2 Page 149


We can list all outdated dependencies with the command:
npm outdated

We can update all dependencies with this command:


npm install dependency@version

Or we can check all our current dependencies for vulnerabilities with this command:
npm audit

Another important part of working with packages is to protect our NPM account. We
should enable two-factor authentication, so users must complete knowledge and
ownership tests while accessing our account. We can change this at
https://www.npmjs.com/settings/your_username/profile.

JavaScript Servers - Module 2 Page 150


Ensure this is enabled for both authorisation and publishing.
While working with NPM modules, we might need access tokens (for example,
when trying to authenticate to npm from an external API). We can create them at
https://www.npmjs.com/settings/your_username/tokens. However, we should only
create a token at the moment that we need one, not earlier.

WATCH

1. Video: Maintain package dependencies (4m 24s) by


Emmanuel Henri on LinkedIn Learning.
2. Video: Add two-factor and read-only tokens with npm
(2m 16s) by Emmanuel Henri on LinkedIn Learning.

Data security
To ensure data security, we should validate data provided by the end users. There
are many libraries to do this. We’ll use ValidatorJSand install it with npm:
npm install validator

JavaScript Servers - Module 2 Page 151


Now we can include it and use one of many validation methods (complete list
available at repository page):
var validator = require('validator');

validator.isEmail('foo@bar.com'); //=> true

Before we save any data to the database, we should always ensure it is


adequately validated, for example:
 Is the data type correct? I.e., numbers may not contain letter characters.
 Is the password strong enough? I.e., does it contains at least one capital
and a special character?
 Are all the required fields filled out in the form?
 Is the provided email unique?

While saving code to the database, we should use template statements. If we use
MongoDB, we can use Error! Hyperlink reference not valid.. If SQL-based one –
sequelize. As we haven’t learnt about databases yet, we will revisit this topic in the
next lessons.
We can increase the security of our data by installing HelmetJS – tool setting HTTP
security headers – HTTP headers that are exchanged between client and server to
specify security details. We can install it with the command:
npm install helmet --save

Then we need to include it in our app.js file:


const express = require("express");
const helmet = require("helmet");

const app = express();

app.use(helmet());

We can learn more about this in HelmetJS documentation.

JavaScript Servers - Module 2 Page 152


The last thing we should do is encrypt important data, such as a password. We
already saw it in the previous lessons regarding using the crypto module to hash
the data.

READ

Page: validatorjs/validator.js by GitHub.

WATCH

1. Video: Data handling with type and validation (4m 28s)


by Emmanuel Henri on LinkedIn Learning.
2. Video: Use prepared statements for SQL/NoSQL (1m
15s) by Emmanuel Henri on LinkedIn Learning.
3. Video: Set proper HTTP headers with Helmet (3m 2s) by
Emmanuel Henri on LinkedIn Learning.
4. Video: Encrypt user data and session management (2m
25s) by Emmanuel Henri on LinkedIn Learning.

Server security
We should use HTTPS protocol instead of pure HTTP to ensure server security.
HTTPS encrypts our connection using Secure Socket Layer (SSL)’s public key
cryptography. Some browsers even refuse to load websites using pure HTTP.

To use the HTTPS protocol, we need to ensure that the domain we’re using to host
our server has a Secure Socket Layer (SSL) certificate. This digital certificate
authenticates a website’s identity and enables an encrypted connection.
We can prevent one way of Denial of Service attack, the one overloading us with
requests, by using the middleware Express Rate Limit. It limits the maximum

JavaScript Servers - Module 2 Page 153


number of requests in a specific amount of time. We can install it with the
command:
npm install express-rate-limit

And later include it in our “app.js” file:


var RateLimit = require('express-rate-limit');

const limiter = new RateLimit({


windowMs: 15*60*1000, // 15 min
max: 100, // up to 100 requests per IP
delay: 0
});
app.use(limiter)

We set a limit of 100 requests per 15 minutes per IP.


While creating cookies, we can set cookies attributes, such as:
 Secure – cookie can be sent only by HTTPS.
 HttpOnly – cookie can’t be used by JavaScript.
 Domain – specify which domains can access it.
 Expire – when cookie expires.

Cookies store important data, such as usernames, passwords, personal information


(our address, phone number etc.) and browsing history, so we want to ensure they
are secured. They are downloaded to our computer when we visit a site for the first
time. On the next visit, the website will try to read these cookies.
We can use cookie-session packages to make working with them easier and
ensure they are handled safely.

JavaScript Servers - Module 2 Page 154


WATCH

1. Video: Use secure HTTPS protocol (1m 28s) by


Emmanuel Henri on LinkedIn Learning.
2. Video: Rate limiting against DoS attacks (4m 2s) by
Emmanuel Henri on LinkedIn Learning.
3. Video: Use cookie attributes (2m 24s) by Emmanuel
Henri on LinkedIn Learning.

What did I learn in this lesson?


This lesson provided the following insights:
 How to use callbacks and promises.
 The difference between sequential and parallel execution.
 Streams and how to use them.
 Security and how OWASP helps increase it.
 The most popular types of security attacks.
 Securing our packages, data and server.

References
Node.js Foundation (2019). Docs | Node.js. [online] Node.js. Available at:
https://nodejs.org/en/docs/.
LinkedIn. (n.d.). Advanced Node.js Online Class | LinkedIn Learning, formerly
Lynda.com. [online] Available at: https://www.linkedin.com/learning/advanced-
node-js.
Videezy. (n.d.). Circle Cut Animation Video. [online] Available at:
https://www.videezy.com/abstract/44059-circle-cut-animation-video.

JavaScript Servers - Module 2 Page 155


OWASP (n.d.). OWASP Foundation, the open-source foundation for application
security. [online] owasp.org. Available at: https://owasp.org/.
Sequelize ORM (2019). Sequelize. [online] Sequelize ORM. Available at:
https://sequelize.org/.
Mongoosejs.com. (2011). Mongoose ODM v5.8.2. [online] Available at:
https://mongoosejs.com/.
GitHub. (2022). validator.js. [online] Available at:
https://github.com/validatorjs/validator.js/.
LinkedIn. (n.d.). Node.js: Security Online Class | LinkedIn Learning, formerly
Lynda.com. [online] Available at: https://www.linkedin.com/learning/node-js-
security.
developer.mozilla.org. (n.d.). eval() - JavaScript | MDN. [online] Available at:
https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval.

2.4. Lesson task - Asynchronous


NodeJS, streams and security

The task
In this lesson, we learnt about asynchronous patterns and streams. In this part of
the task, you need to implement a program using transform streams, which takes
input from the console, changes all letters to upper case, and saves it as a
upper.txt file.
After starting the program, we should be able to provide as much data as we want
and end this by clicking CTRL+C. This shortcut sends a SIGKILL signal, which
terminates the process of reading the data. NodeJS program behaves this way by
default. You don’t need to implement any code to provide this functionality.

JavaScript Servers - Module 2 Page 156


READ

Documentation: Process termination by IBM to learn more


about SIGKILL signal.

Usage example:
Console:

upper.txt:

Solution: Click here to reveal.

JavaScript Servers - Module 2 Page 157


2.5. Lesson - Self-study

Introduction

This is a self-study lesson that consolidates knowledge of the second module. We’ll
attempt to improve the chat application built in the previous chapters. We’ll also
revisit using ExpressJS, EJS, and Socket.IO.

JavaScript Servers - Module 2 Page 158


Materials

READ

1. Documentation: Express by ExpressJS.


2. Documentation: EJS by EJS.
3. Documentation: Socket.IO by Socket.IO.
4. Documentation: npm Docs by npm Docs.
5. Documentation: Node.js by NodeJS.

JavaScript Servers - Module 2 Page 159


WATCH

1. Video: Demo application overview (1m 8s) by Alexander


Zanfir on LinkedIn Learning.
2. Video: Static serving with Express (5m 30s) by
Alexander Zanfir on LinkedIn Learning.
3. Video: Create your browser app (8m 43s) by Alexander
Zanfir on LinkedIn Learning.
4. Video: Create a get messages service (5m 28s) by
Alexander Zanfir on LinkedIn Learning.
5. Video: Create a post messages service (9m 27s) by
Alexander Zanfir on LinkedIn Learning.
6. Video: Connect to Socket.io from the browser app (5m
31s) by Alexander Zanfir on LinkedIn Learning.
7. Video: Create your Socket.io event (1m 59s) by
Alexander Zanfir on LinkedIn Learning.
8. Video: Publish your package (3m 14s) by Emmanuel
Henri on LinkedIn Learning.
9. Video: Transform streams (5m 46s) by Alex Banks on
LinkedIn Learning.

JavaScript Servers - Module 2 Page 160


2.5. Lesson task - Self-study

The task
Part 1

In this part of the task, you need to improve the application we developed in the
web socket lessons by adding a name field to the message. The client should be
able to send their username whenever it sends a new message (this can be done
via prompt):

The username should be displayed next to it. A chat should look like this:

Source code: Click here to reveal room.ejs.


Source code: Click here to reveal index.js.

JavaScript Servers - Module 2 Page 161


Part 2

In this part of the task, you need to create a module that exports the
function filling in the following criteria:
 Takes two string parameters, names of input and output files
 Reads an input file, using a transform stream to transform the data, so
every character is doubled and saved in the output file.
 If the file doesn’t exist, it shows an error message and stops (default
behaviour).

For example, for the file containing text:


Hi, I am John Doe!
We should get the result:
HHii II aamm JJoohhnn DDooee!!
After implementing this functionality, publish it to the NPM library under any unique
name.
Solution: Click here to reveal.

JavaScript Servers - Module 2 Page 162

You might also like