Codecademy Logo

Back-End JavaScript with Node.js

Related learning

  • Back-end developers deal with the hidden processes that run behind the scenes, building APIs and databases that power the front-end.
    • Includes 41 Courses
    • With Professional Certification
    • Beginner Friendly.
      105 hours
  • A full-stack engineer can get a project done from start to finish, back-end to front-end.
    • Includes 51 Courses
    • With Professional Certification
    • Beginner Friendly.
      150 hours

The node Command

We can execute Node.js programs in the terminal by typing the node command, followed by the file name.

The example command runs app.js, assuming there is a file with that name in the current working directory.

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,
  • an eval state where it evaluates the user’s input
  • a print state where it prints out the evaluation to the console

After these states are finished, the 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
Welcome to Node.js v22.19.0.
> console.log("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 executing console.log(global), or simply global in the terminal with the REPL running. In order to see just the keys, use Object.keys(global) instead.

Since global is an object, new properties can be assigned to it via global.name_of_property = 'value_of_property'.

// access global within a script
console.log(global);
// add a 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 .env, which stores and controls information about the environment.

console.log(process.env.PWD); // Prints: /path/where/terminal/command/executed
if (process.env.NODE_ENV === 'development') {
startDevelopmentServer();
console.log('Testing! Testing! Does everything work?');
} else if (process.env.NODE_ENV === 'production') {
startProductionServer();
}

Node.js process.argv

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

// Command line values: node web.js testing several features
console.log(process.argv[2]); // 'testing' 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. Here, “heap” refers to a pool of computer memory, rather than the data structure of the same name.

Using process.memoryUsage() will return an object in a format like the example given.

Node.js Modules

In Node.js, files are called modules. Modularity is a technique where a single 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 import 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.

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 that can be accessed by importing the events core module. Each event emitter instance has an .on() method that assigns a listener callback function to a named event. EventEmitter also has an .emit() method, which announces that a named event 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 showNewUser = (data) => {
console.log(`participant: ${data}.`);
};
// Assign the showNewUser function as the listener callback for 'new user' events
myEmitter.on('new user', showNewUser)
// Emit a 'new user' event
myEmitter.emit('new user', 'Lily Pad') // Prints: participant: Lily Pad.

The error Module

Many asynchronous Node.js APIs assume that the provided callback function will have an error passed as the first argument, followed by the resulting data of a (successful) operation. If the asynchronous task results in an error, the error will be passed in as the first argument to the callback function, with no second argument. If no error was thrown, then the first argument will be undefined, and the resulting data of the operation will be passed as the second argument.

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 process.stdin 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 actually a thin wrapper on stdout.write().

// recieve an input
process.stdin.on('data', onDataCallback);
// produce an output
process.stdout.write(data);

The fs Module

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

const fs = require('fs');
// First argument is the file path
// The second argument is the file’s character encoding
// The third argument is an error-first callback function
fs.readFile('./file.txt', 'utf-8', callbackFunction);

Readable/Writable Streams

In most cases, data is not processed all at once, but rather piece by piece, which we call a stream. 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 in conjunction with fs.createReadStream(). We can create a writeable stream by using the fs.createWriteStream() method, and write to the stream using .write().

const fs = require('fs');
const readline = require('readline');
// Readable stream
readline.createInterface({
input: fs.createReadStream('text.txt')
});
// Writable Stream
const writableStream = fs.createWriteStream('output.txt');
writableStream.write('data');
writableStream.close();

The Buffer Object

A Buffer is an object that represents a static amount of memory that cannot be resized. The Buffer class is within the buffer module. Although this module is globally available, the Node.js documentation still recommends explicitly importing Buffer.

const { Buffer } = require('buffer');
const myBuffer = Buffer.from('Hello World!');

The .alloc() Method

The Buffer object has the .alloc() method, which creates a new Buffer object with the size specified by the first argument. Optionally, a second argument can be provided to specify the fill, and a third argument to specify the character encoding.

// Creates a buffer of size 10 filled with 'b'
// character encoding utf-8 by default
// 'b' is represented by 62 in hex
const bufferAlloc = Buffer.alloc(10, 'b'); // <Buffer 62 62 62 62 62 62 62 62 62 62>

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. Optionally, a character encoding can be specified as the second argument, defaulting to 'utf-8'.

// Creates buffer from the string 'Hello World'
const bufferFrom = Buffer.from('Hello World');
console.log(bufferFrom); // <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64>

The .concat() Method

The .concat() method takes an array of Buffer objects and joins them all into a single, new Buffer object. This comes in handy because a Buffer object cannot be resized.

Optionally, the length of the concatenated buffer may be explicitly specified as the second argument. If the length of the result is greater than the specified total length, it is truncated. If the actual length is less than the specified total length, the remaining bytes are filled with zeroes.

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 place tasks into queues, which are processed at every iteration of the Node.js event loop.

The setImmediate() Function

The setImmediate() function executes the specified callback function after the current poll phase of the event loop has completed. The function accepts a callback function as its first argument and optionally accepts additional arguments that will be passed to the callback.

setImmediate(() => {
console.log('End of the poll phase!');
})

Learn more on Codecademy

  • Back-end developers deal with the hidden processes that run behind the scenes, building APIs and databases that power the front-end.
    • Includes 41 Courses
    • With Professional Certification
    • Beginner Friendly.
      105 hours
  • A full-stack engineer can get a project done from start to finish, back-end to front-end.
    • Includes 51 Courses
    • With Professional Certification
    • Beginner Friendly.
      150 hours