NodeJS #2
Basic concepts
$ whoami
Inna Ivashchuk
Senior Software Engineer
JS developer, music fan, movie-dependent and Star Wars fan 🤓
May the Force be with you!
Agenda
-
Global objects
- NodeJS modules
- Core modules
-
REPL. File execution
Global objects
NodeJS Globals
Global object in a browser
What will happen when you type the following code in a browser console?
// console.log(this) or just
this
And the correct answer is - we will get the full global object Window
Global object in a browser
And some examples:
// standard way to use console.log
console.log('hello');
// equal to
window.console.log('hello');
// custorm variable created like that
window.myVar = 'JavaScript';
// we can access like that
console.log(myVar);
Global object in NodeJS
There is no global object window, but we have - global namespace Object
// standard way to use console.log
console.log('hello');
// equal to
global.console.log('hello');
// custorm variable created like that
global.myVar = 'JavaScript';
// we will get 'undefined'
console.log(myVar);
// this is pointing to the global
console.log(this);
// common functions
global.require('http'); // equal to - require('http');
global.process; // equal to - process;
global.setImmediate(cb); // equal to - setImmediate(cb);
Globals in NodeJS
Commonly used:
- __dirname
- __filename
- module
- exports
- Buffer (class)
- URL (class)
- URLSearchParams (class)
- and etc
NodeJS modules
Modules: CommonJS modules
In the Node.js module system, each file is treated as a separate module
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
Here are the contents of circle.js:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
Modules
Module 1
Module 2
Module 3
Variables local to the module will be private because the module is wrapped in a function by Node.js (IIFE).
Modules
app.js
Every file is a module.
Every variable and function defined in the file are in the scope of this module and not available outside.
Modules: create a new one
logger.js
const loggerUrl = 'http://mylogger.io/log';
function logger(message) {
// Send an HTTP request
console.log(message);
}
module.exports.log = logger;
// we can export URL as well
// mostly should not be done,
// as it's implementation details
module.exports.LOGGER_URL = loggerUrl;
Modules: loading a module
app.js
const logger = require('./logger');
function app() {
// some logic can be there
logger.log('log my message');
}
app();
Modules: another way to do the same
const logger = require('./logger');
function app() {
// some logic can be there
logger('log my message');
}
app();
const loggerUrl = 'http://mylogger.io/log';
function logger(message) {
// Send an HTTP request
console.log(message);
}
module.exports = logger;
Modules: CommonJS modules
The module.exports property can be assigned a new value (such as a function or object).
const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);
The square module is defined in square.js:
module.exports = class Square {
constructor(width) {
this.width = width;
}
area() {
return this.width ** 2;
}
};
Modules: require(X)
Modules: Caching
- Modules are cached after the first time they are loaded
- Modules are cached based on their resolved filename
- On case-insensitive file systems or operating systems, different resolved filenames can point to the same file, but the cache will still treat them as different modules and will reload the file multiple times
a.js
b.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js
Modules: Cycles
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
   When main.js loads a.js, then a.js, in turn, loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
Modules: Cycles
addTwo.mjs
app.mjs
Modules: ECMAScript modules
// addTwo.mjs
function addTwo(num) {
return num + 2;
}
export { addTwo };
// app.mjs
import { addTwo } from './addTwo.mjs';
// Prints: 6
console.log(addTwo(4));
Available from NodeJS v.12.12.0
Core modules
Common information
- Core modules are described in NodeJS documentation
- Core modules are always preferentially loaded if their identifier is passed to require()
- Core modules can also be identified using the node: prefix(Added v16.0.0)
Assert
The assert module provides a set of assertion functions for verifying invariants.
const assert = require('assert/strict');
// This fails because 1 !== '1'.
assert.deepStrictEqual({ a: 1 }, { a: '1' });
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
// {
// + a: 1
// - a: '1'
// }
assert.ok(typeof 123 === 'string');
// AssertionError: The expression evaluated to a falsy value:
//
// assert.ok(typeof 123 === 'string')
Util
The util module supports the needs of Node.js internal APIs. Many of the utilities are useful for application and module developers as well.
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
stat('.').then((stats) => {
// Do something with `stats`
}).catch((error) => {
// Handle the error.
});
util.format('%s:%s', 'foo', 'bar', 'baz');
// Returns: 'foo:bar baz'
util.types.isDate(new Date()); // Returns true
Path
The path module provides utilities for working with file and directory paths.
const path = require('path');
path.parse('/home/user/dir/file.txt');
// Returns:
// { root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
path.format({
root: '/ignored',
dir: '/home/user/dir',
base: 'file.txt'
});
// Returns: '/home/user/dir/file.txt'
path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'
Process
The process object is a global that provides information about, and control over, the current Node.js process.
const process = require('process');
// print process.argv
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`);
});
process.on('uncaughtException', (err, origin) => {
fs.writeSync(
process.stderr.fd,
`Caught exception: ${err}\n` +
`Exception origin: ${origin}`
);
});
process.on('exit', (code) => {
console.log('Process exit event with code: ', code);
});
process.exit(1);
File system
The fs module enables interacting with the file system.
const { unlink } = require('fs/promises');
(async function(path) {
try {
await unlink(path);
console.log(`successfully deleted ${path}`);
} catch (error) {
console.error('there was an error:', error.message);
}
})('/tmp/hello');
const { unlinkSync } = require('fs');
try {
unlinkSync('/tmp/hello');
console.log('successfully deleted /tmp/hello');
} catch (err) {
// handle the error
}
Synchronous example:
HTTP
HTTP server
const http = require('http');
// Create an HTTP server
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('okay');
});
server.listen(8000);
const http = require('http');
http.get('http://localhost:3000', { agent }, (res) => {
res.on('data', (data) => {
// Do nothing
});
});
HTTP request
REPL. File execution
What is REPL?
Read-Eval-Print Loop
Why do we need REPL?
Read-Eval-Print Loop is the Node.js interactive shell; any valid JavaScript written in a script can be passed to the REPL.
It can be extremely useful for experimenting with Node.js, debugging code, and figuring out some of JavaScript's more eccentric behaviors.
How to Start the REPL
Starting the REPL is simple - just run node on the command line without a filename.
$ node
> var x = "Hello, World!"
undefined
> x
"Hello, World!"
> .exit
$ node
And now is time to play:
Special Commands and Exiting the REPL
The following special commands are supported by all REPL instances (from Node.js REPL docs:
- .exit - Close the I/O stream, causing the REPL to exit.
- .break - When in the process of inputting a multi-line expression, entering the .break command (or pressing the <ctrl>-C key combination) will abort further input or processing of that expression.
- .clear - Resets the REPL context to an empty object and clears any multi-line expression currently being input.
- .help - Show this list of special commands.
- .save - Save the current REPL session to a file: > .save ./file/to/save.js
- .load - Load a file into the current REPL session. > .load ./file/to/load.js
Special Commands and Exiting the REPL
- .editor - Enter editor mode (<ctrl>-D to finish, <ctrl>-C to cancel).
> .editor
# Entering editor mode (<ctrl>-D to finish, <ctrl>-C to cancel)
function welcome(name) {
return `Hello ${name}!`;
}
welcome('Node.js User');
# <ctrl>-D
'Hello Node.js User!'
>
Accessing Modules
 If you need to access any of the built-in modules, or any third-party modules, they can be accessed with require, just like in the rest of Node.
For example:
$ node
> path = require('path')
{ resolve: [Function],
normalize: [Function],
join: [Function],
dirname: [Function],
basename: [Function],
extname: [Function],
exists: [Function],
existsSync: [Function] }
> path.basename("/a/b/c.txt")
'c.txt'
Q & A
Homework
-
Create a new directory tutorial2
-
Create next modules: main.js, file.js, logger.js (or you can turn on your imagination and create something different)
-
Create 2 text files and read them in file.js using fs
-
Create a logger module, that should print error or info messages to the terminal
-
In a main.js create a simple HTTP server, that can read URLsÂ
-
Use logger and file modules in main.js
-
Use functions from global in modulesÂ
NodeJS Core #2
By Inna Ivashchuk
NodeJS Core #2
- 476