This posts describes the basic steps to get a NodeJS/Express application up and running. The application we’ll be building exposes some unrelated endpoints to show some functionalities.
NB: I’m definitely not a guru of any of those technologies.
Install Node
Let’s first install NodeJS on our machine.
Download the latest version. At the time of writing, this version is v16.1.0:
1curl https://nodejs.org/dist/v16.1.0/node-v16.1.0-linux-x64.tar.xzYou can install node globally (for exemple in
/usr/local) or locally. I do prefer to install stuff locally even on my local machine so that I’m pretty sure there will be no interference with the system nor mistyping while beingrootwhich may lead to some catastrophe! For local installations, I have a$HOME/usrdirectory.This will install the
nodecommand which is the JavaScript interpreter based on the V8 google JavaScript engine andnpm, the NodeJS eco-system package manager.Add
$HOME/usr/node/binthePATHsomewhere in your.bashrc:Test it by typing
nodeandnpmin a terminal
So far so good!
Create a minimal NodeJS applications
We will create a hello world service that will reply on the following endpoints:
/will replyhelloto show how to manage the default endpoint/hellowill replyhello worldto show how to manage static content/hello/<name>will replyhello <name>to show how to manage dynamic content
Initialise the application
We’ll create an app called hello so first create the
working directory and initialize the app hello:
npm init will ask some questions (and will propose the default
values) about the package name, the entry point, the licence, version,
etc.. Keeping the defaults is fine for the moment. This operation will
create the package.json file which describes the application.
Notice the scripts section which here has one entry, test which
should define how to start the test process and by default there is no
testing method that is defined. Scripts in this section are started
with npm test or npm what_ever_defined.
The content of package.json is:
 1{
 2  "name": "hello",
 3  "version": "1.0.0",
 4  "description": "",
 5  "main": "index.js",
 6  "scripts": {
 7    "test": "echo \"Error: no test specified\" && exit 1"
 8  },
 9  "author": "",
10  "license": "ISC"
11}
Install the dependencies
npm is the package manager for NodeJS applications. It can
initialise applications, upload your packages on the global NPM
repository and manage application’s dependencies. See the output of
npm -l to see the list of all possible actions. Dependencies are
packages that bring additional functionalities to the
application. We’ll install 2 dependencies:
expressis the core web serverejsis the template engine to be used withexpress
To add some packages, simply list them on npm install’s command
line:
1npm install -y express ejs
The -y options answers yes to any question npm may ask during
the installation process.
If we look at the content of package.json, we’ll see there is a new
section:
One can specify the version of the packages to be installed, if there
are to be installed locally for the project only or globally for the
system; here we have installed explress’s and ejs’s latest
versions locally. npm also takes care to import all packages needed
by the packages we are installing. Local packages are stored in the
node_modules directory where we will find express and ejs
sub-directories along with 63 others which contain packages necessary
for express and ejs. All together, this makes 3.7MB for a hello
world web application! Yet remember it comes with everything included:
web server, interpreter, template engine, etc.
Create the application
As defined in package.json, the file
index.js is the entry point to our application. Let’s define it:
 1// Libraries and global variable section
 2var express = require('express')
 3var app = express()
 4
 5// Server start section. The server will be listerning on port 3000
 6var port = 3000
 7var server = app.listen(port, function () {
 8    console.log(`Server listening at http://localhost:%s`, port)
 9})
10
11// Routes section
12app.get('/', function (req, res) {
13    res.send(`hello`)
14})
15
16app.get('/hello', function (req, res) {
17    res.send(`hello world`)
18})
19
20app.get('/hello/:who', function (req, res) {
21    res.send(`hello ${req.params.who}`)
22})
We can now test the application. Start the server by running node index.js in a terminal and point a navigator to the different
endpoints or use curl:
1curl http://localhost:3000/
2  hello
3curl http://localhost:3000/hello
4  hello world
5curl http://localhost:3000/hello/maurycy
6  hello maurycy
And that’s it!
Application’s automatic restart
During the development process, we’ll modify index.js and all other
source files constantly. It will be quite painful to stop ans start
the application every time… To ease this process we’ll install the
nodemon package. nodemon actually supervises the file system and
upon of any change in the source code, it will restart the application
automatically.
To install nodemon:
1npm install nodemon --save-dev # use only for development
As nodemon is only used during development, we give the --save-dev
option to npm install. This option adds packages in
devDependencies section in package.json which list packages to be
used during development phase only.
To start the application with nodemon, type:
1PORT=2000 IP=192.168.1.101 \ # This space is important
2node node_modules/nodemon/bin/nodemon.js index.js
OK, this is typed only once, but it’s quite painful also. The clean
way is to add the line "start": "nodemon index.js" to the scripts section in package.json file and
start the application with npm start.
Try to change something in the source code to see the application restarting.
Serving static files
Serving static files is the basis of HTTP service. This concerns
.html files, but also styles and images. Express handles easily
static files: the only thing is to tell it where the static files ate
located.
Where to store the files
First, create the public directory (mkdir public) and tell express
that static files will be located there by adding the following line
to index.js:
1app.use(express.static('public'));
Put two basic HTML files index.html and test.html in this
directory:
 1cat > public/index.html << EOF
 2<html>
 3  <body>
 4    <h1>This is <b>index.html</b> file</h1>
 5  </body>
 6</html>
 7EOF
 8
 9cat > public/test.html << EOF
10<html>
11  <body>
12    <h1>This is <b>test.html</b> file</h1>
13  </body>
14</html>
15EOF
That’s all. You can try the URLs http://localhost:3000/test.html.
Styles and images
Static stiles and images files can be stored in public/css and
public/images directories. Put in there an image, say, info.png
and a style, say style.css and change test.html to
1<html>
2  <head>
3    <link rel="stylesheet" href="/css/style.css" media="screen">
4  </head>
5  <body>
6    <h1><img src="/images/info.png">This is <b>test.html</b> file</h1>
7  </body>
8</html>
Simple!
Order for searching the resources
Depending on the position of app.use(express.static('public')) and
of the route definition, the behavior of the application will be different.
Let’s take as an example the default endpoint, that is /. It is
supposed to render the file public/index.html. If
app.use(express.static('public')); is declared before the route
app.get('/', function (req, res) {...}, then what will be rendered
is the file public/index.html, as expected. However, if the order is
reversed, then it’s the content returned by the route that will be
rendered.
I believe that the good habit is to have
app.use(express.static('public')) before any route definition.
MIME types
The nice thing is that express.static takes care of the mime type
based on the extension of the file that is being requested. For
example, create a .css file in public (touch public/style.css),
get it with curl -v http://localhost/style.css, and observe the
Content-Type HTTP header: it should be set to text/css.
Add support for templating
Basics
Templating is provided by the ejs package that we have already
installed. Notice that there are many more template engines as for
example handlebars, pug, and others.
To use ejs we need to tell express that the template engine is
ejs. To do so, add the following line just after var app = express() in index.js:
1var app = express()
2// set the view engine to ejs
3app.set('view engine', 'ejs');
4
5// rest of index.js
ejs assumes template files are located in the views directory and
have by default the .ejs extension. For example, to render the
documentthat provides help for our application, we will call
res.render('help'); which supposes the existence of the file
views/help.ejs. So, let’s create the directory views and add the
help.ejs file:
Then add the route and call the render() function in index.js:
Notice that the route is independant of the name of the file to be rendered..
To test, try : curl http://localhost:3000/gethelp
Tags
This a a copy and paste from the EJS GitHub page
<%: ‘Scriptlet’ tag, for control-flow, no output<%_: ‘Whitespace Slurping’ Scriptlet tag, strips all whitespace before it<%=: Outputs the value into the template (escaped)<%-: Outputs the unescaped value into the template<%#: Comment tag, no execution, no output<%%: Outputs a literal ‘<%’%%>: Outputs a literal ‘%>’%>: Plain ending tag-%>: Trim-mode (’newline slurp’) tag, trims following newline_%>: ‘Whitespace Slurping’ ending tag, removes all whitespace after it
See here for details about the syntax.
Parameters to templates
ejs is a templating engine, so we should be able to pass some
arguments to it. This is done by passing a second argument to
render() which is a JavaScript object. For example, change the route
/hello/:who by:
Then create the file hello.ejs in views directory:
EJS rendered hello with parameter "<%= who%>"
and try it with http://localhost:3000/hello/Polo.
Partials
ejs is able to include other .ejs files, often called
partials. This is typically useful if you want a consistent styling
across the site, a constant menu bar, a copyright message,
etc. without the hassle to repeat over and over the same code over all
the .ejs files. For this, we can define a header file, viewsheader.ejs
that will define information, as the style:
and a views/footer.ejs file which will display the copyright and close a open
HTML tags:
Now, every views/xxx.ejs page will need to have the following format
to include the partials:
Notice that, just as normal templates, partials can receive parameters in JavaScript object format.
Serving JSON content
By default send() or render() set the Content-Type header to
text/html (check this with curl -v ...). If we want to serve JSON
content, for example an object returned by an API, we will use the
res.json() function. As an example, upon hit on /headers endpoint,
let’s return the HTTP headers of the request. Add the following route:
and test it with curl -v http://localhost:3000/headers
Getting parameters from the environment
There are situations where we want to start the server with some parameters that we can’t or that we don’t want to hard code or store in a file as for example database credentials or network port and IP address the server will listen on. The easy way is to read such parameters from the environment variables.
To do so, change the server startup section in index.js with:
 1// Get the IP address and the port to listen on from the environment
 2const port = process.env.PORT || null
 3const ip = process.env.IP || null
 4if(ip == null || port == null) {
 5    console.error("Environment varables 'IP' or 'PORT' not set");
 6    console.error("Bailing out")
 7    process.exit(1);
 8}
 9// Start listening
10var server = app.listen(port, ip, function () {
11    var h = server.address().address
12    var p = server.address().port
13    console.info(`Server listening at http://${h}:${p}`)
14})
To start the server we now need to provide both environment variables:
1IP=127.0.0.1 PORT=2000 npm start
Source files
At this point, after some final beautifyings, we should have the following files:
index.js1// Libraries and global variable section 2var express = require('express') 3var app = express() 4 5app.use(express.static('public')) 6app.set('view engine', 'ejs') 7 8// Get the IP address and the port to listen on from the environment 9const port = process.env.PORT || null 10const ip = process.env.IP || null 11if(ip == null || port == null) { 12 console.error("Environment varables 'IP' or 'PORT' not set"); 13 console.error("Bailing out") 14 process.exit(1); 15} 16// Start listening 17var server = app.listen(port, ip, function () { 18 var h = server.address().address 19 var p = server.address().port 20 console.info(`Server listening at http://${h}:${p}`) 21}) 22 23// Routes section 24app.get('/', function (req, res) { 25 res.send(`Hello`) 26}) 27 28app.get('/hello', function (req, res) { 29 res.send(`hello world`) 30// res.json({"sid": sid, "resp": "Hello world"}) 31}) 32 33app.get('/hello/:who', function (req, res) { 34 res.render('hello', {who: req.params.who}); 35}) 36 37app.get('/gethelp', function (req, res) { 38 res.render('help'); 39}) 40 41app.get('/headers', function (req, res) { 42 res.json(req.headers) 43}) 44 45app.get('/test', function (req, res) { 46 res.render('test'); 47})public/index.htmlpublic/test.htmlviews/hello.ejs1EJS rendered hello with parameter "<%= who%>"views/header.ejsviews/footer.ejsviews/test.ejs
Conclusion
There are many, many more things that could be added about Express and EJS: please refer to the references. Sure, you have not become a ninja on Express nor EJS, but I believe it’s enough to start.