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.xz
You 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 beingroot
which may lead to some catastrophe! For local installations, I have a$HOME/usr
directory.1cd ~/usr 2tar xvf ~/Downloads/node-v16.1.0-linux-x64.tar.xz 3ln -s node-v16.1.0-linux-x64/ node
This will install the
node
command which is the JavaScript interpreter based on the V8 google JavaScript engine andnpm
, the NodeJS eco-system package manager.Add
$HOME/usr/node/bin
thePATH
somewhere in your.bashrc
:1# Nodejs 2export PATH=$HOME/usr/node/bin:$PATH
Test it by typing
node
andnpm
in 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 replyhello
to show how to manage the default endpoint/hello
will replyhello world
to 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
:
1mkdir hello
2cd hello
3npm init
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:
express
is the core web serverejs
is 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:
1"dependencies": {
2 "ejs": "^3.1.6",
3 "express": "^4.17.2"
4}
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:
1mkdir views
2cat > views/help.ejs << EOF
3This is the help for the application
4EOF
Then add the route and call the render()
function in index.js
:
1// Gets the help page
2app.get('/gethelp', function (req, res) {
3 res.render('help');
4})
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:
1app.get('/hello/:who', function (req, res) {
2 res.render('hello', {who: req.params.who});
3})
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:
1<html>
2 <head>
3 <link rel="stylesheet" href="/css/style.css" media="screen">
4 </head>
5 <body>
and a views/footer.ejs
file which will display the copyright and close a open
HTML tags:
1 <hr>
2 <center>Copyright me</center>
3 </body>
4</html>
Now, every views/xxx.ejs
page will need to have the following format
to include the partials:
1<%- include('header') %>
2 <h1>This is <b>xxx.ejs</b> file</h1>
3<%- include('footer') %>
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:
1// other routes
2app.get('/headers', function (req, res) {
3 res.json(req.headers)
4})
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.js
1// 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.html
1<html> 2 <body> 3 <h1>This is <b>index.html</b> file</h1> 4 </body> 5</html>
public/test.html
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>
views/hello.ejs
1EJS rendered hello with parameter "<%= who%>"
views/header.ejs
1<html> 2 <head> 3 <link rel="stylesheet" href="/css/style.css" media="screen"> 4 </head> 5 <body>
views/footer.ejs
1 <hr> 2 <center>Copyright: me</center> 3 </body> 4</html>
views/test.ejs
1<%- include('header') %> 2 <h1><img src="/images/info.png">This is <b>test.ejs</b> file</h1> 3<%- include('footer') %>
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.