Skip to content
DevNursery.com - New Web Developer Docs
GitHub

NodeJS

Node.js: An Introduction

What is Node.js?

Node.js is an open-source, server-side JavaScript runtime environment that allows developers to build and run JavaScript applications outside of a web browser. It is based on the V8 JavaScript engine from Google and was created by Ryan Dahl in 2009. Node.js enables developers to use JavaScript for both frontend and backend development, fostering full-stack JavaScript development.

History of Node.js

Node.js has an interesting history:

  • 2009: Node.js was first introduced by Ryan Dahl, primarily as a way to address the limitations of traditional server-side technologies, which used synchronous, blocking I/O operations.
  • 2010: The first official release of Node.js (Version 0.1.14) became available.
  • 2011: The Node Package Manager (NPM) was introduced, revolutionizing package management in the JavaScript ecosystem.
  • 2012: The Node.js project joined the Linux Foundation, gaining support and contributions from various organizations and developers.
  • 2018: The release of Node.js 10 brought long-term support (LTS) to the platform, ensuring stability and reliability for enterprise applications.
  • 2020: Node.js 14, another LTS release, introduced enhanced performance and improved features.

Why Node.js Matters

Node.js has gained significant popularity for several reasons:

  • Efficiency: Node.js uses a non-blocking, event-driven architecture that makes it highly efficient for handling concurrent connections and I/O-bound operations. This efficiency is crucial for building real-time applications like chat apps and online gaming platforms.

  • Single Language: With Node.js, developers can use JavaScript both on the frontend and backend, which simplifies the development process and reduces the need to switch between languages.

  • Large Ecosystem: Node.js has a vast ecosystem of open-source libraries and modules available through NPM, making it easy for developers to find and use pre-built components in their projects.

  • Scalability: Node.js is well-suited for building scalable applications, thanks to its ability to handle a large number of concurrent connections without significant performance overhead.

Use Cases for Node.js

Node.js is versatile and finds application in various areas, including:

  • Web Servers: Node.js is commonly used to build lightweight, high-performance web servers. Popular frameworks like Express.js simplify the process of creating web applications.

  • Real-time Applications: It is ideal for developing real-time applications such as chat applications, online gaming, and collaborative tools that require instant data updates.

  • API Servers: Node.js is often chosen for building API servers that serve as the backend for mobile and web applications, providing data and services.

  • Microservices: In a microservices architecture, Node.js can power individual services due to its efficiency and scalability.

  • IoT (Internet of Things): Node.js is suitable for developing applications for IoT devices, where real-time data processing is essential.

Node.js continues to evolve, and its community actively contributes to its growth, making it a popular choice for modern application development.

NodeJS CLI Commands

Part 1

PurposeSyntaxExample
Check Node.js versionnode -v$ node -v -> v14.17.6
Run a JavaScript filenode filename.js$ node script.js
Start a Node.js REPL (Read-Eval-Print Loop)node$ node
Execute JavaScript code directlynode -e "JavaScript code"$ node -e "console.log('Hello, Node.js!')"
Install a Node.js package globallynpm install -g package-name$ npm install -g express
Initialize a new Node.js projectnpm init$ npm init
Install dependencies from package.jsonnpm install$ npm install
Install a specific version of a packagenpm install package-name@version$ npm install lodash@4.17.21
Install a package and save it as a dependencynpm install package-name --save$ npm install axios --save
Install a package as a development dependencynpm install package-name --save-dev$ npm install jest --save-dev
List installed packagesnpm list$ npm list
Update all packages to their latest versionsnpm update$ npm update
Uninstall a packagenpm uninstall package-name$ npm uninstall lodash
Run a script defined in package.jsonnpm run script-name$ npm run start
Check for outdated packagesnpm outdated$ npm outdated

Part 2

PurposeSyntaxExample
Display detailed information about a packagenpm show package-name$ npm show express
Search for packages in the npm registrynpm search package-name$ npm search request
Check the location of global npm packagesnpm root -g$ npm root -g
List globally installed packagesnpm list -g --depth=0$ npm list -g --depth=0
View the npm configurationnpm config list$ npm config list
Set a specific configuration valuenpm config set key value$ npm config set proxy http://proxy.example.com
List outdated packages globallynpm outdated -g --depth=0$ npm outdated -g --depth=0
List top-level dependencies in a projectnpm ls --depth=0$ npm ls --depth=0
Remove all packages from node_modules`npm ls —depth=0awk -F/ ‘/node_modules/ && !//npm$/ {print $NF}’
Install a specific version of Node.jsUse a Node Version Manager (NVM) like nvmInstall and manage Node.js versions with nvm
Check npm versionnpm --version$ npm --version
Display Node.js usage helpnode --help$ node --help
Generate a Node.js heap dumpnode --heapdump=filename$ node --heapdump=mydump.heapsnapshot
Debug Node.js using the built-in debuggernode inspect script.js$ node inspect app.js
Run a Node.js script with debugging enablednode --inspect-brk script.js$ node --inspect-brk app.js
Use a custom JavaScript file for debuggingnode --require custom-debug.js script.js$ node --require my-debug.js app.js
Profile Node.js applicationnode --prof script.js$ node --prof app.js
Generate flamegraph from a V8 log filenode --prof-process --preprocess -j v8.log > output.svg$ node --prof-process --preprocess -j v8.log > output.svg

Part 3

PurposeSyntaxExample
Update a globally installed packagenpm update -g package-name$ npm update -g express
Install a package locally and save as a dev dependencynpm install package-name --save-dev$ npm install jest --save-dev
Execute scripts defined in package.jsonnpm run script-name$ npm run test
Display a list of available scriptsnpm run$ npm run
Set the registry for npmnpm set registry registry-url$ npm set registry https://registry.npmjs.org/
Log in to an npm registry (authenticate)npm login$ npm login
Log out from an npm registry (deauthenticate)npm logout$ npm logout
Create a new npm user accountnpm adduser$ npm adduser
Check Node.js’s ability to reach the networknpm ping$ npm ping
List available npm scripts and their descriptionsnpm scripts$ npm scripts
Show npm package detailsnpm show package-name$ npm show lodash
Check for npm registry connectivity issuesnpm doctor$ npm doctor
Display npm’s help documentationnpm help$ npm help
Install packages with a specific node versionnpm install package-name --engine-strict$ npm install package-name --engine-strict
List globally installed packages in a long formatnpm ls -g --long$ npm ls -g --long
Install packages with a custom registry URLnpm install package-name --registry custom-registry-url$ npm install lodash --registry https://my-registry.com
Display the current npm configurationnpm config list$ npm config list
Show the package.json configurationnpm config list --json$ npm config list --json
Access the npm cache directorynpm config get cache$ npm config get cache
Verify the integrity of cached packagesnpm cache verify$ npm cache verify
Clean the npm cachenpm cache clean$ npm cache clean
List installed global packagesnpm ls -g --depth=0$ npm ls -g --depth=0
View package documentation in a web browsernpm docs package-name$ npm docs express
View the npm audit report for a packagenpm audit$ npm audit
Run npm audit fix to automatically fix issuesnpm audit fix$ npm audit fix

Package.json

What is package.json?

In Node.js, the package.json file is a manifest file that contains metadata about a Node.js application or package. It serves multiple purposes:

  • Dependency Management: It lists all the dependencies (external packages) required by the application, including their versions.

  • Script Definitions: It defines various scripts that can be run using npm commands, such as running tests, starting the application, or building assets.

  • Metadata: It includes metadata about the application, such as its name, version, description, author, and license information.

  • Configuration: It can store configuration settings for the project, which can be accessed programmatically.

package.json Properties and Their Purposes Here is a list of commonly used properties in a package.json file and their purposes:

PropertyPurpose
nameSpecifies the name of the package or application.
versionIndicates the version of the package.
descriptionProvides a brief description of the package.
keywordsLists keywords to help with package discovery.
repositorySpecifies the source code repository URL.
authorSpecifies the name and contact details of the author.
licenseDefines the package’s license type.
dependenciesLists production dependencies required for the app.
devDependenciesLists development dependencies required for testing.
scriptsDefines custom scripts that can be run with npm.
mainSpecifies the entry point JavaScript file.
binMaps package commands to executable scripts.
enginesSpecifies Node.js and npm version requirements.
privateMarks the package as private (not for publication).
homepageProvides a URL to the project’s homepage.
bugsPoints to the issue tracker for bug reporting.
contributorsLists contributors to the project.
scripts.startDefines the command to start the application.
scripts.testDefines the command to run tests.
scripts.buildSpecifies the build command for the project.
scripts.prepublishA script that runs before publishing the package.
scripts.cleanDefines a custom clean-up script.
scripts.lintDefines a linting script for code quality checks.
scripts.deploySpecifies a deployment script.

Example Package.json

{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "A sample Node.js application",
  "keywords": ["node", "sample", "example"],
  "repository": {
    "type": "git",
    "url": "https://github.com/yourusername/my-node-app"
  },
  "author": "Your Name <youremail@example.com>",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "axios": "^0.21.1"
  },
  "devDependencies": {
    "jest": "^27.0.6",
    "eslint": "^7.32.0"
  },
  "scripts": {
    "start": "node server.js",
    "test": "jest",
    "build": "webpack --config webpack.config.js",
    "prepublish": "npm run build",
    "clean": "rm -rf dist",
    "lint": "eslint .",
    "deploy": "npm run build && rsync -avz dist/ user@server:/path/to/remote/directory"
  },
  "main": "server.js",
  "bin": {
    "myapp": "./bin/myapp"
  },
  "engines": {
    "node": ">=14.0.0",
    "npm": ">=6.0.0"
  },
  "private": false,
  "homepage": "https://github.com/yourusername/my-node-app#readme",
  "bugs": {
    "url": "https://github.com/yourusername/my-node-app/issues"
  },
  "contributors": [
    "Contributor1 <contributor1@example.com>",
    "Contributor2 <contributor2@example.com>"
  ]
}

The Node.js process Object

In Node.js, the process object is a global object that provides information and control over the current Node.js process. It is an instance of the EventEmitter class, which means it can emit events and handle event listeners.

The process object exposes a wide range of properties and methods that allow you to interact with the Node.js runtime environment, access environment variables, manage command-line arguments, and more. Here, we’ll focus on some of the key properties of the process object:

1. process.argv

The process.argv property is an array that contains the command-line arguments passed to the Node.js process. The first element (process.argv[0]) is the path to the Node.js executable, and the second element (process.argv[1]) is the path to the JavaScript file being executed. Any additional command-line arguments are stored in subsequent elements of the array.

Example:

// If you run "node script.js arg1 arg2"
console.log(process.argv);
// Output: [ 'node', '/path/to/script.js', 'arg1', 'arg2' ]

2. process.env

The process.env property is an object that contains the environment variables for the current process. You can access these variables to retrieve information such as system settings, configuration values, and user-defined variables.

Example:

// Access the value of the "NODE_ENV" environment variable
const nodeEnv = process.env.NODE_ENV;
console.log(`Node environment: ${nodeEnv}`);

3. process.pid

The process.pid property returns the process ID (PID) of the current Node.js process. This can be useful for various purposes, such as monitoring and managing processes.

Example:

console.log(`Process ID: ${process.pid}`);

4. process.platform

The process.platform property returns a string indicating the platform on which Node.js is running, such as ‘darwin’ for macOS, ‘win32’ for Windows, or ‘linux’ for Linux.

Example:

console.log(`Platform: ${process.platform}`);
5. process.cwd()

The process.cwd() method returns the current working directory of the Node.js process. This is the directory from which the Node.js application was launched.

Example:

console.log(`Current working directory: ${process.cwd()}`);

6. process.exit()

The process.exit(code) method allows you to exit the Node.js process with a specified exit code. It’s commonly used to terminate the application, and the exit code can be used to convey information about the termination status (0 for success, non-zero for error).

Example:

// Exit with a custom exit code (e.g., 1 for an error)
process.exit(1);

These are just a few of the many properties and methods available on the process object in Node.js. It provides valuable information and control over the runtime environment, making it an essential tool for building and managing Node.js applications.

Working with Files and Folders using Node.js fs Library

The Node.js fs (File System) module is a built-in module that provides a range of functions for interacting with the file system. It allows you to perform operations such as reading, writing, creating, deleting, and managing files and directories in both synchronous and asynchronous ways.

Checking if a File Exists Synchronously

To check if a file exists synchronously, you can use the fs.existsSync(path) function. It returns true if the file exists at the specified path and false otherwise.

Example:

const fs = require('fs');

const filePath = 'example.txt';

if (fs.existsSync(filePath)) {
  console.log(`${filePath} exists.`);
} else {
  console.log(`${filePath} does not exist.`);
}

Creating Files and Folders Synchronously

To create files and folders synchronously, you can use the fs.writeFileSync(file, data) method to create a new file with the specified data and the fs.mkdirSync(path) method to create a new directory.

Example:

const fs = require('fs');

// Create a new file
fs.writeFileSync('newfile.txt', 'Hello, world!');

// Create a new directory
fs.mkdirSync('newfolder');

Reading Files Synchronously

You can read files synchronously using the fs.readFileSync(path, [options]) method. This method returns the content of the file as a buffer or string, depending on the specified encoding.

Example:

const fs = require('fs');

const filePath = 'example.txt';

try {
  const data = fs.readFileSync(filePath, 'utf8'); // Read file as a UTF-8 encoded string
  console.log(data);
} catch (err) {
  console.error(`Error reading ${filePath}: ${err.message}`);
}

Synchronous vs. Asynchronous Functions

When choosing between synchronous and asynchronous functions in Node.js, consider the following:

Synchronous Functions: Synchronous functions block the execution of the program until the operation is complete. They are suitable for simple scripts or when you need to ensure that a particular operation is finished before moving on.

Asynchronous Functions: Asynchronous functions do not block the program’s execution, making them suitable for non-blocking I/O operations. They are commonly used in web servers and applications that require high concurrency.

In general, it’s recommended to use asynchronous functions for I/O operations in Node.js to avoid blocking the event loop and ensure good performance, especially in server-side applications. However, synchronous functions can be useful for simple scripts or when you need to perform sequential operations.

Example: Reading and Writing a JSON File Synchronously

Here’s an example that demonstrates reading data from a JSON file, modifying it, and writing it back to the file synchronously:

const fs = require('fs');

const filePath = 'data.json';

try {
  // Read JSON data from the file
  const jsonData = fs.readFileSync(filePath, 'utf8');
  const data = JSON.parse(jsonData);

  // Modify the data
  data.name = 'John Doe';
  data.age = 30;

  // Write the modified JSON data back to the file
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
  
  console.log('Data written to the file successfully.');
} catch (err) {
  console.error(`Error: ${err.message}`);
}

In this example, we read JSON data from a file, modify it, and then write the updated data back to the same file synchronously.

Running Child Processes with Node.js child_process Module

The Node.js child_process module is a built-in module that provides a way to create and manage child processes. Child processes are separate instances of the Node.js runtime that can run independently, allowing you to execute external commands, run scripts, and perform other tasks concurrently with your main Node.js application.

The child_process module provides both synchronous and asynchronous methods for interacting with child processes. Here, we’ll cover some of the key functions and when to use them.

Spawning a Child Process

You can spawn a new child process using the child_process.spawn() method. This method allows you to execute an external command or run a script in a separate process.

Example (asynchronous):

const { spawn } = require('child_process');

// Spawning a child process to run a command
const child = spawn('ls', ['-l', '/']);

// Handling the child process output
child.stdout.on('data', (data) => {
  console.log(`Child process output:\n${data}`);
});

// Handling errors
child.on('error', (error) => {
  console.error(`Error occurred: ${error.message}`);
});

// Handling the child process exit event
child.on('exit', (code, signal) => {
  if (code !== null) {
    console.log(`Child process exited with code ${code}`);
  } else if (signal !== null) {
    console.log(`Child process terminated by signal ${signal}`);
  }
});

Executing a Command in a Shell

To run a command within a shell, you can use the child_process.exec() method. This method allows you to execute shell commands, including piping and shell-specific syntax.

Example (asynchronous):

const { exec } = require('child_process');

// Executing a command in a shell
exec('ls -l | grep "file.txt"', (error, stdout, stderr) => {
  if (error) {
    console.error(`Error occurred: ${error.message}`);
    return;
  }
  console.log(`Command output:\n${stdout}`);
});

Forking a Node.js Module as a Child Process You can use the child_process.fork() method to fork a Node.js module as a child process. This is particularly useful when you want to create multiple instances of a Node.js script to perform tasks concurrently.

Example (asynchronous):

const { fork } = require('child_process');

// Forking a Node.js module as a child process
const child = fork('child_script.js');

// Communicating with the child process
child.on('message', (message) => {
  console.log(`Received message from child process: ${message}`);
});

// Sending a message to the child process
child.send('Hello from the parent process!');

When to Use Synchronous vs. Asynchronous Functions

When working with child processes, it’s important to choose between synchronous and asynchronous functions based on your application’s requirements:

Asynchronous Functions: Asynchronous functions like spawn(), exec(), and fork() are non-blocking and are suitable for running processes concurrently with your main application. They are ideal for tasks that may take some time to complete.

Synchronous Functions: Synchronous functions like spawnSync() and execSync() block the execution of your main application until the child process completes. Use them when you need the result of a child process immediately or for simple scripting tasks.

In general, prefer asynchronous functions for most use cases to ensure that your application remains responsive and can handle multiple concurrent tasks.

The Node.js child_process module provides powerful tools for running child processes, making it possible to parallelize tasks, execute external commands, and interact with other processes in a Node.js environment.

Working with Paths using Node.js path Module

The Node.js path module is a built-in module that provides utilities for working with file and directory paths. It helps you perform various operations related to paths, such as joining, parsing, resolving, and manipulating paths in a cross-platform manner. This is especially useful because different operating systems use different path conventions (e.g., backslashes in Windows, forward slashes in Unix-like systems).

Here, we’ll explore some of the key functions of the path module and common use cases.

Constructing Paths

path.join([...paths]) The path.join() method joins one or more path segments together using the platform-specific separator and resolves any relative paths. It’s useful for constructing file and directory paths.

Example:

const path = require('path');

const folder = 'my-folder';
const file = 'my-file.txt';

const filePath = path.join(folder, file);
console.log(filePath); // Outputs: my-folder/my-file.txt (on Unix-like systems)

Parsing Paths

path.parse(pathString) The path.parse() method parses a path string into an object with properties representing the various components of the path, including the directory, base file name, and file extension.

Example:

const path = require('path');

const filePath = '/root/project/file.txt';
const parsedPath = path.parse(filePath);
console.log(parsedPath);
/* Outputs:
{
  root: '/',
  dir: '/root/project',
  base: 'file.txt',
  ext: '.txt',
  name: 'file'
}
*/

Resolving Paths

path.resolve([...paths]) The path.resolve() method resolves an absolute path from left to right by joining path segments and navigating up directories if necessary. It returns an absolute path relative to the current working directory.

Example:

const path = require('path');

const absolutePath = path.resolve('/root', 'project', 'file.txt');
console.log(absolutePath); // Outputs: /root/project/file.txt (on Unix-like systems)

Normalizing Paths

path.normalize(pathString) The path.normalize() method normalizes a path by resolving '..' and '.' segments to simplify it. This is useful for cleaning up and ensuring a consistent path format.

Example:

const path = require('path');

const dirtyPath = '/root/project/../file.txt';
const cleanPath = path.normalize(dirtyPath);
console.log(cleanPath); // Outputs: /root/file.txt (on Unix-like systems)

Common Use Cases

File Operations: Use the path module when working with file operations to ensure that file paths are correctly formatted and platform-independent.

Building URLs: When constructing URLs in a web application, the path.join() method can be helpful for constructing paths in a consistent manner.

Navigating Directories: When navigating directories or determining the location of files or resources, use path.resolve() to create reliable absolute paths.

File Path Parsing: When you need to extract information from file paths, such as the file name, directory, or extension, use path.parse() for convenient parsing.

Path Normalization: When dealing with user-generated paths or handling inputs from different sources, use path.normalize() to ensure that paths are in a consistent format.

The Node.js path module simplifies path-related operations and ensures cross-platform compatibility. It’s an essential tool for managing file and directory paths in Node.js applications.

Writing a Basic Node.js CLI App

Step 1: Set Up Your Project

Create a new directory for your CLI app and navigate to it in your terminal.

Initialize a Node.js project by running:

npm init -y

This command generates a package.json file with default values.

Step 2: Create Your CLI Script

Create a JavaScript file for your CLI app. For example, you can name it cli.js.

Write your CLI app logic in this file. Here’s a simple example that prints a welcome message and takes a user’s name as an argument:

#!/usr/bin/env node

const [, , ...args] = process.argv; // Get command-line arguments

const name = args.join(' ') || 'World';

console.log(`Hello, ${name}! This is your CLI app.`);

The #!/usr/bin/env node line is called a “shebang” and tells the system to use Node.js to run the script.

We use process.argv to get the command-line arguments passed to the script.

Step 3: Make Your Script Executable

In your terminal, run the following command to make your script executable:

chmod +x cli.js

Step 4: Test Your CLI App

Test your CLI app by running it from the command line:

./cli.js

You should see the welcome message.

Test it with arguments:

./cli.js John

You should see a personalized greeting.

Publishing Your CLI App to NPM

Step 5: Prepare Your Package

Update your package.json file with the following information:

“bin” field: This specifies the name of the CLI command and the path to your CLI script. For example:

"bin": {
  "my-cli": "./cli.js"
}

Here, “my-cli” will be the command users run to execute your CLI app.

“main” field: Ensure that the “main” field in your package.json points to your CLI script. For example:

"main": "cli.js"

Ensure your package.json contains accurate metadata, including a unique name, description, version, and author information.

Step 6: Login to Your NPM Account

If you haven’t already, create an account on npmjs.com.

In your terminal, log in to your npm account using the following command:

npm login

You’ll be prompted to enter your npm username, password, and email address.

Step 7: Publish Your Package

In your terminal, navigate to your project’s root directory.

Run the following command to publish your package to the npm registry:

npm publish

If this is your first time publishing a package, npm will prompt you to confirm your package’s details.

Step 8: Verify Your Published Package

Visit your package’s npm page by going to https://www.npmjs.com/package/YOUR_PACKAGE_NAME, replacing YOUR_PACKAGE_NAME with your package’s name.

You should see your package listed on npm’s website.

Step 9: Install and Use Your CLI App

To install and use your CLI app globally, you can run the following command:

npm install -g YOUR_PACKAGE_NAME

Replace YOUR_PACKAGE_NAME with the name you specified in your package.json under “name”.

Now, you can use your CLI app from any terminal window:

my-cli John

Congratulations! You’ve successfully written a basic Node.js CLI app and published it to the npm registry. Users can now install and use your CLI app globally on their systems.