Node.Js

Node.Js - Programmer Sheet

The node Command

We can execute Node.js programs in the terminal by typing the node command, followed by the name of the file. The example command above runs app.js.

node app.js
Node.js REPL

Node.js comes with REPL, an abbreviation for read–eval–print loop. REPL contains three different states:

*a read state where it reads the input from a user, *the eval state where it evaluates the user’s input *the print state where it prints out the evaluation to the console.

After these states are finished REPL loops through these states repeatedly. REPL is useful as it gives back immediate feedback which can be used to perform calculations and develop code.

//node is typed in the console to access REPL
$ node
 
//the > indicates that REPL is running
// anything written after > will be evaluated 
> console.log("HI")
 
// REPL has evaluated the line and has printed out HI
HI
Node.js Global Object

The Node.js environment has a global object that contains every Node-specific global property. The global object can be accessed by either typing in console.log(global) or global in the terminal after RPL is running. In order to see just the keys Object.keys(global) can be used. Since global is an object, new properties can be assigned to it via global.name_of_property = 'value_of_property'.

//Two ways to access global
> console.log(global)
//or
> global
 
//Adding new property to global
> global.car = 'delorean'
Node.js Process Object

A process is the instance of a computer program that is being executed. Node has a global process object with useful properties. One of these properties is NODE_ENV which can be used in an if/else statement to perform different tasks depending on if the application is in the production or development phase.

if (process.env.NODE_ENV === 'development'){
  console.log('Do not deploy!! Do not deploy!!');
}
Node.js process.argv

process.argv is a property that holds an array of command-line values provided when the current process was initiated. The first element in the array is the absolute path to the Node, followed by the path to the file that’s running and finally any command-line arguments provided when the process was initiated.

// Command line values: node web.js testing several features
console.log(process.argv[2]); // 'features' will be printed
Node.js process.memoryUsage()

process.memoryUsage() is a method that can be used to return information on the CPU demands of the current process. Heap can refer to a specific data structure or to the computer memory.

//using process.memoryUsage() will return an object in a format like this:
 
{ 
  rss: 26247168,
  heapTotal: 5767168,
  heapUsed: 3573032,
  external: 8772 
}
Node.js Modules

In Node.js files are called modules. Modularity is a technique where one program has distinct parts each providing a single piece of the overall functionality - like pieces of a puzzle coming together to complete a picture. require() is a function used to bring one module into another.

const baseball = require('./babeRuth.js')
Node.js Core Modules

Node has several modules included within the environment to efficiently perform common tasks. These are known as the core modules. The core modules are defined within Node.js’s source and are located in the lib/ folder. A core module can be accessed by passing a string with the name of the module into the require() function.

const util = require('util');
Listing Node.js Core Modules

All Node.js core modules can be listed in the REPL using the builtinModules property of the module module. This is useful to verify if a module is maintained by Node.js or a third party.

> require('module').builtinModules
The console Module

The Node.js console module exports a global console object offering similar functionality to the JavaScript console object used in the browser. This allows us to use console.log() and other familiar methods for debugging, just like we do in the browser. And since it is a global object, there’s no need to require it into the file.

console.log('Hello Node!'); // Logs 'Hello Node!' to the terminal
console.log()

The console.log() method in Node.js outputs messages to the terminal, similar to console.log() in the browser. This can be useful for debugging purposes.

console.log('User found!'); // Logs 'User found!' to the terminal
The os Module

The Node.js os module can be used to get information about the computer and operating system on which a program is running. System architecture, network interfaces, the computer’s hostname, and system uptime are a few examples of information that can be retrieved.

const os = require('os');
 
const systemInfo = {  
  'Home Directory': os.homedir(),    
  'Operating System': os.type(),
  'Last Reboot': os.uptime()
};
The util Module

The Node.js util module contains utility functions generally used for debugging. Common uses include runtime type checking with types and turning callback functions into promises with the .promisify() method.

// typical Node.js error-first callback function
function getUser (id, callback) {
  return setTimeout(() => {
    if (id === 5) {
      callback(null, { nickname: 'Teddy' });
    } else {
      callback(new Error('User not found'));
    }
  }, 1000);
}
 
function callback (error, user) {
  if (error) {
    console.error(error.message);
    process.exit(1);
  }
  console.log(`User found! Their nickname is: ${user.nickname}`);
}
 
// change the getUser function into promise using `util.promisify()`
const getUserPromise = util.promisify(getUser);
 
// now you're able to use then/catch or async/await syntax
getUserPromise(id)
  .then((user) => {
      console.log(`User found! Their nickname is: ${user.nickname}`);
  })
  .catch((error) => {
      console.log('User not found', error);
  });
The events Module

Node.js has an EventEmitter class which can be accessed by importing the events core module by using the require() statement. Each event emitter instance has an .on() method which assigns a listener callback function to a named event. EventEmitter also has an .emit() method which announces a named event that has occurred.

// Require in the 'events' core module
let events = require('events');
 
// Create an instance of the EventEmitter class
let myEmitter = new events.EventEmitter();
let version = (data) => {
 console.log(`participant: ${data}.`);
};
 
// Assign the version function as the listener callback for 'new user' events
myEmitter.on('new user', version)
 
// Emit a 'new user' event
myEmitter.emit('new user', 'Lily Pad')
// 'Lily Pad'
Input/Output

Input is data that is given to the computer, while output is any data or feedback that a computer provides. In Node, we can get input from a user using the stdin.on() method on the process object. We are able to use this because .on() is an instance of EventEmitter. To give an output, we can use the .stdout.write() method on the process object as well. This is because console.log() is a thin wrapper on .stdout.write().

// Recieves an input
process.stdin.on();
 
// Gives an output
process.stdout.write();
The fs Module

The filesystem controls how data on a computer is stored and retrieved. Node.js provides the fs core module, which allows interaction with the filesystem. Each method provided through the module has a synchronous and asynchronous version to allow for flexibility. A method available in the module is the .readFile() method that reads data from the provided file.

// First argument is the file path
// The second argument is the file’s character encoding
// The third argument is the invoked function
fs.readFile('./file.txt', 'utf-8', CallbackFunction);
Readable/Writable Streams

In most cases, data isn’t processed all at once but rather piece by piece. This is what we call streams. Streaming data is preferred as it doesn’t require tons of RAM and doesn’t need to have all the data on hand to begin processing it. To read files line-by-line, we can use the .createInterface() method from the readline core module. We can write to streams by using the .createWriteStream() method.

// Readable stream
readline.createInterface();
 
// Writtable Stream
fs.createWriteStream();
The Buffer Object

A Buffer is an object that represents a static amount of memory that can’t be resized. The Buffer class is within the global buffer module, meaning it can be used without the require() statement.

The .alloc() Method

The Buffer object has the .alloc() method that allows a new Buffer object to be created with the size specified as the first argument. Optionally, a second argument can be provided to specify the fill and a third argument to specify the encoding.

const bufferAlloc = Buffer.alloc(10, 'b'); // Creates a buffer of size 10 filled with 'b'
The .toString() Method

A Buffer object can be translated into a human-readable string by chaining the .toString() method to a Buffer object. Optionally, encoding can be specified as the first argument, byte offset to begin translating can be provided as the second argument, and the byte offset to end translating as the third argument.

const bufferAlloc = Buffer.alloc(5, 'b');
console.log(bufferAlloc.toString()); // Ouptut: bbbbb
The .from() Method

A new Buffer object can be created from a specified string, array, or another Buffer object using the .from() method. Encoding can be specified optionally as the second argument.

const bufferFrom = Buffer.from('Hello World'); // Creates buffer from 'Hello World' string
The .concat() Method

The .concat() method joins all Buffer objects in the specified array into one Buffer object. The length of the concatenated Buffer can be optionally provided as the second argument. This method is useful because a Buffer object can’t be resized.

const buffer1 = Buffer.from('Hello');
const buffer2 = Buffer.from('World');
const bufferArray = [buffer1, buffer2];
 
const combinedBuffer = Buffer.concat(bufferArray);
console.log(combinedBuffer.toString()); // Logs 'HelloWorld'
The timers Module

The global timers module contains scheduling functions such as setTimeout(), setInterval(), and setImmediate(). These functions are put into a queue processed at every iteration of the Node.js event loop.

The setImmediate() Function

The setImmediate() function executes the specified callback function after the current event loop has completed. The function accepts a callback function as its first argument and optionally accepts arguments for the callback function as the subsequent arguments.

setImmediate(() => {
  console.log('End of this event loop!');
})
HTTP

HTTP Transmission

HTTP messages can be sent using various transmission protocols. The two most common protocols used for this are Transmission Control Protocol (TCP) and User Datagram Protocol (UDP).

Securing HTTP

HTTP messages can be protected using Transport Layer Security (TLS), a protocol designed to facilitate secure data transmission via encryption. Using TLS with HTTP will allow you to use HTTPS (Hypertext Transfer Protocol Secure), which helps denote the presence of extra security.

The .createServer() Method

The .createServer() method from the http module is used to create an HTTP server. The .createServer() method takes a single argument in the form of a callback function. This callback function has two primary arguments; the request (commonly written as req) and the response (commonly written as res).

const http = require('http');
 
// Create instance of server
const server = http.createServer((req, res) => {
  res.end('Server is running!');
});
 
// Start server listening on port 8080
server.listen(8080, () => {
  const { address, port } = server.address();
  console.log(`Server is listening on: http://${address}:${port}`);
});
The Request Object

The request object in server request handlers contains information about the incoming HTTP request. Information contained within this request object can be used in various ways to handle server requests such as routing and data processing.

The Response Object

The response object in server request handlers contains information about the outgoing HTTP response. The response object contains various properties and methods that can be used to configure and send the response such as .statusCode, .setHeader(), and.end().

const http = require('http');
 
const server = http.createServer((req, res) => {
  
  // Set status and headers
  res.statusCode = 200;
  res.setHeader('Content-Type', 'application/json');
  
  // Send response
  res.end('Hello World');
});
 
server.listen(8080);
The url Module

The url module can be used to break down URLs into their constituent parts. It can also be used to construct URLs. Both of these actions are carried out using the URL class provided by the url module.

/* Deconstructing a URL */
 
// Create an instance of the URL class
const url = new URL('https://www.example.com/p/a/t/h?query=string');
 
// Access parts of the URL as properties on the url instance
const host = url.hostname; // example.com
const pathname = url.pathname; // /p/a/t/h
const searchParams = url.searchParams; // {query: 'string'}
 
 
/* Constructing a URL */
 
// Create an instance of the URL class
const createdUrl = new URL('https://www.example.com');
 
// Assign values to the properties on the url instance
createdUrl.pathname = '/p/a/t/h';
createdUrl.search = '?query=string';
 
createUrl.toString(); // Creates https://www.example.com/p/a/t/h?query=string
The querystring Module

The querystring module is used to decode/encode and parse query string parameters into easily usable data structures. This module only operates on query string parameters, requiring isolation of the query string before use. The core methods of the module are .parse(), .stringify() , .escape(), and .unescape().

// Parse a querystring into an object with query parameter key/value pairs
const str = 'prop1=value1&prop2=value2';
querystring.parse(str); // Returns { prop1: value1, prop2: value2}
 
// Build a querystring from an object of query parameters
const props = { "prop1": value1, "prop2": value2 };
querystring.stringify(props); // Returns 'prop1=value1&prop2=value2'
 
// Percent-encode a querystring
const str = 'prop1=foo[a]&prop2=baz[b]';
querystring.escape(str); // Returns 'prop1%3Dfoo%5Ba%5D%26prop2%3Dbaz%5Bb%5D'
 
// Decode a percent-encoded querystring
const str = 'prop1%3Dfoo%5Ba%5D%26prop2%3Dbaz%5Bb%5D';
querystring.unescape(str); // Returns 'prop1=foo[a]&prop2=baz[b]'
HTTP and Databases

HTTP requests can be used to interact with databases to retrieve remote data. This enables the development of complex applications that can both read and write data to and from remote sources and is the underpinning of modern applications used today.

HTTP and External APIs

HTTP requests can be used to interact with other external APIs from within a server. This ability allows for communication between multiple services within the same application and is the underpinning for certain design architectures such as microservice architectures. One common way to make a request to another server is through the .request() method on the http module.

const options = {
  hostname: 'example.com',
  port: 8080,
  path: '/projects',
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  }
}
 
// Make request to external API
const request = http.request(options, res => {
  // Handle response here
});
HTTP Response Status Codes

HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Each response status code conveys information about what happened during the processing of the request, which in turn helps the client decide how to handle the response and if further action is necessary. Status codes are paired with a short text-based description to help elucidate the meaning of the code.

const http = require('http');
 
// Creates server instance
const server = http.createServer((req, res) => {
  try {
    // Do something here
  } catch(error) {
    res.statusCode = 500; // Sets status code to indicate server error
    return res.end(JSON.stringify(error.message));
  }
});
 
// Starts server listening on specified port
server.listen(4001, () => {
  const { address, port } = server.address();
  console.log(`Server is listening on: http://${address}:${port}`);
});