MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

Node.js Fedora Install

with 2 comments

I want to add the MEAN (MongoDB, Express.js, Angular.js, and Node.js) stack to my backend server development course. This post documents the installation and configuration of components on Fedora 30.

The first step requires installing the Node package. The Node package also contains the Node package manager (npm). You install the Node packages as the root user or as a sudoer user with the following command.

yum install -y npm

It should produce the following installation log:

Last metadata expiration check: 1:10:42 ago on Wed 08 Jul 2020 06:57:52 PM MDT.
Dependencies resolved.
================================================================================================================================
 Package                         Architecture          Version                                     Repository              Size
================================================================================================================================
Installing:
 npm                             x86_64                1:6.13.4-1.10.19.0.1.fc30                   updates                3.8 M
Installing dependencies:
 nodejs                          x86_64                1:10.19.0-1.fc30                            updates                 88 k
 nodejs-libs                     x86_64                1:10.19.0-1.fc30                            updates                9.1 M
Installing weak dependencies:
 nodejs-full-i18n                x86_64                1:10.19.0-1.fc30                            updates                7.3 M
 
Transaction Summary
================================================================================================================================
Install  4 Packages
 
Total download size: 20 M
Installed size: 91 M
Downloading Packages:
(1/4): nodejs-10.19.0-1.fc30.x86_64.rpm                                                         173 kB/s |  88 kB     00:00    
(2/4): nodejs-full-i18n-10.19.0-1.fc30.x86_64.rpm                                               2.8 MB/s | 7.3 MB     00:02    
(3/4): nodejs-libs-10.19.0-1.fc30.x86_64.rpm                                                    2.7 MB/s | 9.1 MB     00:03    
(4/4): npm-6.13.4-1.10.19.0.1.fc30.x86_64.rpm                                                   1.3 MB/s | 3.8 MB     00:02    
--------------------------------------------------------------------------------------------------------------------------------
Total                                                                                           4.9 MB/s |  20 MB     00:04     
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Running scriptlet: npm-1:6.13.4-1.10.19.0.1.fc30.x86_64                                                                   1/1 
  Preparing        :                                                                                                        1/1 
  Installing       : nodejs-libs-1:10.19.0-1.fc30.x86_64                                                                    1/4 
  Installing       : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64                                                               2/4 
  Installing       : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64                                                                   3/4 
  Installing       : nodejs-1:10.19.0-1.fc30.x86_64                                                                         4/4 
  Running scriptlet: nodejs-1:10.19.0-1.fc30.x86_64                                                                         4/4 
  Verifying        : nodejs-1:10.19.0-1.fc30.x86_64                                                                         1/4 
  Verifying        : nodejs-full-i18n-1:10.19.0-1.fc30.x86_64                                                               2/4 
  Verifying        : nodejs-libs-1:10.19.0-1.fc30.x86_64                                                                    3/4 
  Verifying        : npm-1:6.13.4-1.10.19.0.1.fc30.x86_64                                                                   4/4 
 
Installed:
  nodejs-1:10.19.0-1.fc30.x86_64           nodejs-full-i18n-1:10.19.0-1.fc30.x86_64     nodejs-libs-1:10.19.0-1.fc30.x86_64    
  npm-1:6.13.4-1.10.19.0.1.fc30.x86_64    
 
Complete!

After installing the Node package, you should use the Node package manager (npm) to install the Node Monitor nodemon. nodemon is a popular utility that automatically lets you restart Node programs when you make changes to the source code.

While npm is installed as part of the Node package, you must use npm to install the Node Monitor. The following command installs the nodemon globally on your Fedora system. The -g flag lets you install it globally, which is important when you manage package.json files.

npm install -g nodemon

You install nodemon globally but most of your web app or project files will be installed locally. Node is a different paradigm than building an Apache or IIS web application because Node provides a framework for you to build a web server.

Here’s a quick Hello World! example that I borrowed a JavaScript helloworld.js file from an excellent Web Development with Node & Express: Leveraging the JavaScript Stack by Ethan Brown. For those who haven’t worked with JavaScript in years, semicolons are optional now.

/* Construct a web server. */
const http = require('http')
const port = process.env.PORT || 3000
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' })
  res.end('Hello world!')
})
 
server.listen(port, () => console.log(`server started on port ${port}); ` +
                                      'press Ctrl-C to terminate...'))

I put this in /var/www/html/node directory, which is owned by the superuser, root. You need to start the server before accessing it from a browser. You can start the program with the following syntax as a privileged user:

node /var/www/html/node/helloworld.js

Then, you can use the localhost to access it with the following URL:

http://localhost:3000

It will display the following:

Next, you need to use the Node Package Manager (npm) to install the Express.js packages. You do that with the following syntax:

npm install -g express express-generator

It should produce a console out put like the following:

npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
/usr/local/bin/express -> /usr/local/lib/node_modules/express-generator/bin/express-cli.js
+ express@4.17.1
+ express-generator@4.16.1
added 60 packages from 42 contributors in 4.798s

After you install all the packages, you can inspect them with the following command. The packages are found in the /usr/local/lib/node_modules/express directory. The listing is generated from the package.json file on Fedora and Ubuntu Linux.

npm list -g

It should display something like this:

/usr/local/lib
├─┬ express@4.17.1
│ ├─┬ accepts@1.3.7
│ │ ├─┬ mime-types@2.1.27
│ │ │ └── mime-db@1.44.0
│ │ └── negotiator@0.6.2
│ ├── array-flatten@1.1.1
│ ├─┬ body-parser@1.19.0
│ │ ├── bytes@3.1.0
│ │ ├── content-type@1.0.4 deduped
│ │ ├── debug@2.6.9 deduped
│ │ ├── depd@1.1.2 deduped
│ │ ├─┬ http-errors@1.7.2
│ │ │ ├── depd@1.1.2 deduped
│ │ │ ├── inherits@2.0.3
│ │ │ ├── setprototypeof@1.1.1 deduped
│ │ │ ├── statuses@1.5.0 deduped
│ │ │ └── toidentifier@1.0.0
│ │ ├─┬ iconv-lite@0.4.24
│ │ │ └── safer-buffer@2.1.2
│ │ ├── on-finished@2.3.0 deduped
│ │ ├── qs@6.7.0 deduped
│ │ ├─┬ raw-body@2.4.0
│ │ │ ├── bytes@3.1.0 deduped
│ │ │ ├── http-errors@1.7.2 deduped
│ │ │ ├── iconv-lite@0.4.24 deduped
│ │ │ └── unpipe@1.0.0 deduped
│ │ └── type-is@1.6.18 deduped
│ ├─┬ content-disposition@0.5.3
│ │ └── safe-buffer@5.1.2 deduped
│ ├── content-type@1.0.4
│ ├── cookie@0.4.0
│ ├── cookie-signature@1.0.6
│ ├─┬ debug@2.6.9
│ │ └── ms@2.0.0
│ ├── depd@1.1.2
│ ├── encodeurl@1.0.2
│ ├── escape-html@1.0.3
│ ├── etag@1.8.1
│ ├─┬ finalhandler@1.1.2
│ │ ├── debug@2.6.9 deduped
│ │ ├── encodeurl@1.0.2 deduped
│ │ ├── escape-html@1.0.3 deduped
│ │ ├── on-finished@2.3.0 deduped
│ │ ├── parseurl@1.3.3 deduped
│ │ ├── statuses@1.5.0 deduped
│ │ └── unpipe@1.0.0
│ ├── fresh@0.5.2
│ ├── merge-descriptors@1.0.1
│ ├── methods@1.1.2
│ ├─┬ on-finished@2.3.0
│ │ └── ee-first@1.1.1
│ ├── parseurl@1.3.3
│ ├── path-to-regexp@0.1.7
│ ├─┬ proxy-addr@2.0.6
│ │ ├── forwarded@0.1.2
│ │ └── ipaddr.js@1.9.1
│ ├── qs@6.7.0
│ ├── range-parser@1.2.1
│ ├── safe-buffer@5.1.2
│ ├─┬ send@0.17.1
│ │ ├── debug@2.6.9 deduped
│ │ ├── depd@1.1.2 deduped
│ │ ├── destroy@1.0.4
│ │ ├── encodeurl@1.0.2 deduped
│ │ ├── escape-html@1.0.3 deduped
│ │ ├── etag@1.8.1 deduped
│ │ ├── fresh@0.5.2 deduped
│ │ ├── http-errors@1.7.2 deduped
│ │ ├── mime@1.6.0
│ │ ├── ms@2.1.1
│ │ ├── on-finished@2.3.0 deduped
│ │ ├── range-parser@1.2.1 deduped
│ │ └── statuses@1.5.0 deduped
│ ├─┬ serve-static@1.14.1
│ │ ├── encodeurl@1.0.2 deduped
│ │ ├── escape-html@1.0.3 deduped
│ │ ├── parseurl@1.3.3 deduped
│ │ └── send@0.17.1 deduped
│ ├── setprototypeof@1.1.1
│ ├── statuses@1.5.0
│ ├─┬ type-is@1.6.18
│ │ ├── media-typer@0.3.0
│ │ └── mime-types@2.1.27 deduped
│ ├── utils-merge@1.0.1
│ └── vary@1.1.2
└─┬ express-generator@4.16.1
  ├── commander@2.15.1
  ├── ejs@2.6.1
  ├─┬ minimatch@3.0.4
  │ └─┬ brace-expansion@1.1.11
  │   ├── balanced-match@1.0.0
  │   └── concat-map@0.0.1
  ├─┬ mkdirp@0.5.1
  │ └── minimist@0.0.8
  └── sorted-object@2.0.1

You can also create a secure node site (HTTPS) with the following additional steps. They include creating a self-signed secure public and private key. This creates the public key:

openssl genrsa -out key.pem

The openssl command will generate a private key key.pem file. It generates something like the following text message to console:

Generating RSA private key, 2048 bit long modulus (2 primes)
...........+++++
.............................................+++++
e is 65537 (0x010001)

Next, you need to generate a self-signed certificate. You do this in two steps.

  1. Create a Distinguished Name (DN) file. The csr.pem file is the DN file. You need it to create a self-signed certificate:

    openssl req -new -key key.pem -out csr.pem

    It will prompt you for values, like the following:

    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [XX]:
    State or Province Name (full name) []:MiddleEarth
    Locality Name (eg, city) [Default City]:Rivendell
    Organization Name (eg, company) [Default Company Ltd]:Fellowship    
    Organizational Unit Name (eg, section) []:Self
    Common Name (eg, your name or your server's hostname) []:Localhost
    Email Address []:bilbo@loth.org
     
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:friend
    An optional company name []:Bilbo
  2. Use the DN file to create your secure certificate. The following openssl command creates the certificate file by using your private key key.pem and DN csr.pem files.

    openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem

    It should generate a Secure certificate cert.pem file and return something like the following to the console.

    Signature ok
    subject=C = XX, ST = MiddleEarth, L = Rivendell, O = Fellowship, OU = Self, CN = Localhost, emailAddress = bilbo@loth.org
    Getting Private key

You can put these private key (key.pem) and certificate (cert.pem) files in an ssl subdirectory of the directory where you put the JavaScript program. The following creates a secure server page with the following code.

/* Construct a secure web server. */ 
const https = require('https') 
const fs = require('fs') 
const port = process.env.PORT || 3000 
 
const options = { 
 key: fs.readFileSync('ssl/key.pem'), 
 cert: fs.readFileSync('ssl/cert.pem') 
} 
 
const server = https.createServer((options, res) => { 
 res.writeHead(200, { 'Content-Type': 'text/plain' }) 
 res.end('Hello world!') 
}) 
 
server.listen(port, () => console.log(`server started on port ${port}); ` + 
                                     'press Ctrl-C to terminate...'))

If you try launch your browser using the localhost instead of a DNS or file resolved network name on the designated port, it will raise the following security error:

This site can't be reached
https's server IP address could not be found.
DNS_PROBE_FINISHED_NXDOMAIN

An alternate approach to writing a secure server includes using Express.js library. The syntax changes somewhat and you include two new libraries, as shown below:

/* Construct a secure web server. */ 
const https = require('https') 
const express = require('express') 
const fs = require('fs') 
const app = express() 
const port = process.env.PORT || 3001 
 
const options = { 
 key:  fs.readFileSync('ssl/key.pem'), 
 cert: fs.readFileSync('ssl/cert.pem') 
} 
 
https.createServer(options, app).listen(port, () => { 
 console.log(`Express started in ${app.get('env')} mode ` + 
             `on port + ${port}.`) 
})

This will fail with the following error message if you’re running it with a global installation unless you set the $NODE_PATH environment variable correctly. Without setting the variable you may get the following error message:

internal/modules/cjs/loader.js:638 
   throw err; 
   ^ 
 
Error: Cannot find module 'express' 
   at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15) 
   at Function.Module._load (internal/modules/cjs/loader.js:562:25) 
   at Module.require (internal/modules/cjs/loader.js:692:17) 
   at require (internal/modules/cjs/helpers.js:25:18) 
   at Object.<anonymous> (/var/www/html/node/helloworldsecure.js:3:17) 
   at Module._compile (internal/modules/cjs/loader.js:778:30) 
   at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) 
   at Module.load (internal/modules/cjs/loader.js:653:32) 
   at tryModuleLoad (internal/modules/cjs/loader.js:593:12) 
   at Function.Module._load (internal/modules/cjs/loader.js:585:3)

Sometimes they’ll advise you to do an individual user installation of Express.js to get past this error but that’s not necessary. You just need to set the $NODE_PATH environment variable as follows:

export NODE_PATH=/usr/local/lib/node_modules

This will enable the JavaScript to work without error and without a specific user installation. Assuming you name either of these programs as helloworldsecure.js, you run them with the following command:

node helloworldsecure.js

You can terminate the program with a Ctrl+c or if use the kill -15 pid command if you started it as a background process. You can find the process ID (pid) with the jobs command.

As always, I hope this is helpful to those starting out with this cool technology stack.

Written by maclochlainn

July 8th, 2020 at 9:10 pm