Node.js Basics

Updated: 03 September 2023

Basic Modules

Installing Prerequisites

Installing NodeJs

Install the necessary version of Node Node v8+ and NPM v5+

Confirm that node is installed

1
node -v
2
npm -v

Also installing a node server globally

1
npm i -g node-static

Installing MongoDb

Install Mongo from the Download page

Confirm that MongoDb is installed

1
mongod --version

How start a Node REPL environment

Can run standard javascript with

1
node

Alternatively, we can use eval with

1
node -e "<javascript code>"

To run a node script we use

1
node file.js

This can be an absolute or releative path to the file

NodeJs Globals

We are providded with some additional objects and keywords on top of javascript

  • global
  • process
  • module.exports or exports

Global

Any first level global property is available without the keywords Some poperties of the global object are as follows:

  • process
  • require
  • module
  • console
  • __dirname
  • __filename

Processes

Every Node.js script is a process

We can interact with the process by:

  • env Enviromnent variables
  • argv Command-line arguments
  • exit() Terminate the current process

Process exit codes can be specified

1
// process failed
2
process.exit(1)
3
4
// process exited successfully
5
process.exit(0)
6
7
// process failed with custom exit code
8
process.exit(code)

Import and Export Modules

module.exports

Global property to allow a script to export something for other modules to use

1
module.exports = function (numbersToSum) {
2
let sum = 0,
3
i = 0,
4
l = numbersToSum.length
5
while (i < l) {
6
sum += numbersToSum[i++]
7
}
8
return sum
9
}

require

require() is a path to a file, or a name. This will import the necessary files that we need to read. JSON files can be imported directly as an object.

1
const sum = require('./utility.js')

require can be used to import many types of modules as such:

1
const filesystem = require('fs') // core module
2
const express = require('express') // npm module
3
const server = require('./boot/server.js') // server.js file with a relative path down the tree
4
const server = require('../boot/server.js') // server.js file with a relative path up the tree
5
const server = require('/var/www/app/boot/server.js') // server.js file with an absolute path
6
const server = require('./boot/server') // file if there's the server.js file
7
const routes = require('../routes') // index.js inside routes folder if there's no routes.js file
8
const databaseConfigs = require('./configs/database.json') // JSON file

Core Modules

Node has a lot of preinstalled modules, the main ones are as follows:

  • fs: module to work with the file system, files and folders
  • path: module to parse file system paths across platforms
  • querystring: module to parse query string data
  • net: module to work with networking for various protocols
  • stream: module to work with data streams
  • events: module to implement event emitters (Node observer pattern)
  • child_process: module to spawn external processes
  • os: module to access OS-level information including platform, number of CPUs, memory, uptime, etc.
  • url: module to parse URLs
  • http: module to make requests (client) and accept requests (server)
  • https: module to do the same as http only for HTTPS
  • util: various utilities including promosify which turns any standard Node core method into a promise-base API
  • assert: module to perform assertion based testing
  • crypto: module to encrypt and hash information

fs

Handles file system operations

  • fs.readFile() reads files asynchronously
  • fs.readFileSync() reads files synchronously
  • fs.writeFile() writes files asynchronously
  • fs.writeFileSync() writes files synchronously

Reading a File

1
const fs = require('fs')
2
const path = require('path')
3
fs.readFile(
4
path.join(__dirname, '/data/customers.csv'),
5
{ encoding: 'utf-8' },
6
function (error, data) {
7
if (error) return console.error(error)
8
console.log(data)
9
}
10
)

Writing to a file

1
const fs = require('fs')
2
fs.writeFile('message.txt', 'Hello World!', function (error) {
3
if (error) return console.error(error)
4
console.log('Writing is done.')
5
})

path

Can join a path relativley as:

1
const path = require('path')
2
const server = require(path.join('app', 'server.js'))

Or absoltely as:

1
const path = require('path')
2
const server = require(path.join(__dirname, 'app', 'server.js'))

Event emitters

We can create an EventEmitter with events and using this we can create, listen and trigger events

Single trigger

1
const EventEmitter = require('events')
2
3
class Job extends EventEmitter {}
4
job = new Job()
5
6
job.on('done', function (timeDone) {
7
console.log('Job was pronounced done at', timeDone)
8
})
9
10
job.emit('done', new Date())
11
job.removeAllListeners() // remove all observers

Output Job was pronounced done at ____________

Mutiple triggers

1
const EventEmitter = require('events')
2
3
class Emitter extends EventEmitter {}
4
emitter = new Emitter()
5
6
emitter.on('knock', function() {
7
console.log('Who's there?')
8
})
9
10
emitter.on('knock', function() {
11
console.log('Go away!')
12
})
13
14
emitter.emit('knock')
15
emitter.emit('knock')

Output

1
Who's there?
2
Go away!
3
Who's there?
4
Go away!

Single Execution of Handler

1
const EventEmitter = require('events')
2
3
class Emitter extends EventEmitter {}
4
emitter = new Emitter()
5
6
emitter.once('knock', function() {
7
console.log('Who's there?')
8
})
9
10
11
emitter.emit('knock')
12
emitter.emit('knock')

Output Who's there?

Modular events

We can use the observer pattern to modularize code. This allows us to customize modular behaviour without modifying the module.

jobs.js

1
const EventEmitter = require('events')
2
class Job extends EventEmitter {
3
constructor(ops) {
4
super(ops)
5
this.on('start', () => {
6
this.process()
7
})
8
}
9
process() {
10
setTimeout(() => {
11
// Emulate the delay of the job - async!
12
this.emit('done', { completedOn: new Date() })
13
}, 700)
14
}
15
}
16
17
module.exports = Job

main.js

1
var Job = require('./job.js')
2
var job = new Job()
3
4
job.on('done', function (details) {
5
console.log('Weekly email job was completed at', details.completedOn)
6
})
7
8
job.emit('start')

HTTP Client

Get

Request and Response

Making an HTTP Request using http from NodeJs Core. Will receive data in chunks as follows http-get-no-buff

1
const http = require('http')
2
const url = 'http://nodeprogram.com'
3
http
4
.get(url, (response) => {
5
response.on('data', (chunk) => {
6
console.log(chunk.toString('utf8'))
7
})
8
response.on('end', () => {
9
console.log('response has ended')
10
})
11
})
12
.on('error', (error) => {
13
console.error(`Got error: ${error.message}`)
14
})

Alternatively, the data can be aded to a buffer until the response is complete as below http-get.js

1
const http = require('http')
2
const url = 'http://nodeprogram.com'
3
http
4
.get(url, (response) => {
5
let rawData = ''
6
response.on('data', (chunk) => {
7
rawData += chunk
8
})
9
response.on('end', () => {
10
console.log(rawData)
11
})
12
})
13
.on('error', (error) => {
14
console.error(`Got error: ${error.message}`)
15
})

Processing JSON

In order to get JSON the full response is needed, after which we parse the json to a response object

http-json-get.js

1
const https = require('https')
2
const url =
3
'https://gist.githubusercontent.com/azat-co/a3b93807d89fd5f98ba7829f0557e266/raw/43adc16c256ec52264c2d0bc0251369faf02a3e2/gistfile1.txt'
4
https
5
.get(url, (response) => {
6
let rawData = ''
7
response.on('data', (chunk) => {
8
rawData += chunk
9
})
10
response.on('end', () => {
11
try {
12
const parsedData = JSON.parse(rawData)
13
console.log(parsedData)
14
} catch (e) {
15
console.error(e.message)
16
}
17
})
18
})
19
.on('error', (error) => {
20
console.error(`Got error: ${error.message}`)
21
})

Post

To do a post we require a little but more information to be configured as such:

http-post.js

1
const http = require('http')
2
const postData = JSON.stringify({ foo: 'bar' })
3
4
const options = {
5
hostname: 'mockbin.com',
6
port: 80,
7
path: '/request?foo=bar&foo=baz',
8
method: 'POST',
9
headers: {
10
'Content-Type': 'application/x-www-form-urlencoded',
11
'Content-Length': Buffer.byteLength(postData),
12
},
13
}
14
15
const req = http.request(options, (res) => {
16
res.on('data', (chunk) => {
17
console.log(`BODY: ${chunk}`)
18
})
19
res.on('end', () => {
20
console.log('No more data in response.')
21
})
22
})
23
24
req.on('error', (e) => {
25
console.error(`problem with request: ${e.message}`)
26
})
27
28
req.write(postData)
29
req.end()

HTTP Server

We can use node http-server.js to run the server, we can also use node-dev to run the server and refresh on filechange.

http-server.js

1
const http = require('http')
2
const port = 3000
3
http
4
.createServer((req, res) => {
5
res.writeHead(200, { 'Content-Type': 'text/plain' })
6
res.end('Hello World\n')
7
})
8
.listen(port)
9
10
console.log(`Server running at http://localhost:${port}/`)

http.createServer creates a server with a callback function which contains a response handler code

res.writeHead(200, {'Content-Type': 'text/plain'}) sets the right status code and headers

res.end() event handler for when response is complete

listen() specifies the port on which the server is listening

Processing a request

We can process an incoming request by reading the request properties with the following:

httP-server-request-processing.js

1
const http = require('http')
2
const port = 3000
3
http.createServer((request, response) => {
4
console.log(request.headers)
5
console.log(request.method)
6
console.log(request.statusCode)
7
console.log(request.url)
8
if (request.method == 'POST') {
9
let buff = ''
10
request.on('data', function (chunk) {
11
buff += chunk
12
})
13
request.on('end', function () {
14
console.log(`Body: ${buff}`)
15
response.end('\nAccepted body\n')
16
})
17
} else {
18
response.writeHead(200, {'Content-Type': 'text/plain'})
19
response.end('Hello World\n')
20
}
21
}).listen(port)