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
Purpose | Syntax | Example |
---|---|---|
Check Node.js version | node -v | $ node -v -> v14.17.6 |
Run a JavaScript file | node filename.js | $ node script.js |
Start a Node.js REPL (Read-Eval-Print Loop) | node | $ node |
Execute JavaScript code directly | node -e "JavaScript code" | $ node -e "console.log('Hello, Node.js!')" |
Install a Node.js package globally | npm install -g package-name | $ npm install -g express |
Initialize a new Node.js project | npm init | $ npm init |
Install dependencies from package.json | npm install | $ npm install |
Install a specific version of a package | npm install package-name@version | $ npm install lodash@4.17.21 |
Install a package and save it as a dependency | npm install package-name --save | $ npm install axios --save |
Install a package as a development dependency | npm install package-name --save-dev | $ npm install jest --save-dev |
List installed packages | npm list | $ npm list |
Update all packages to their latest versions | npm update | $ npm update |
Uninstall a package | npm uninstall package-name | $ npm uninstall lodash |
Run a script defined in package.json | npm run script-name | $ npm run start |
Check for outdated packages | npm outdated | $ npm outdated |
Part 2
Purpose | Syntax | Example |
---|---|---|
Display detailed information about a package | npm show package-name | $ npm show express |
Search for packages in the npm registry | npm search package-name | $ npm search request |
Check the location of global npm packages | npm root -g | $ npm root -g |
List globally installed packages | npm list -g --depth=0 | $ npm list -g --depth=0 |
View the npm configuration | npm config list | $ npm config list |
Set a specific configuration value | npm config set key value | $ npm config set proxy http://proxy.example.com |
List outdated packages globally | npm outdated -g --depth=0 | $ npm outdated -g --depth=0 |
List top-level dependencies in a project | npm ls --depth=0 | $ npm ls --depth=0 |
Remove all packages from node_modules | `npm ls —depth=0 | awk -F/ ‘/node_modules/ && !//npm$/ {print $NF}’ |
Install a specific version of Node.js | Use a Node Version Manager (NVM) like nvm | Install and manage Node.js versions with nvm |
Check npm version | npm --version | $ npm --version |
Display Node.js usage help | node --help | $ node --help |
Generate a Node.js heap dump | node --heapdump=filename | $ node --heapdump=mydump.heapsnapshot |
Debug Node.js using the built-in debugger | node inspect script.js | $ node inspect app.js |
Run a Node.js script with debugging enabled | node --inspect-brk script.js | $ node --inspect-brk app.js |
Use a custom JavaScript file for debugging | node --require custom-debug.js script.js | $ node --require my-debug.js app.js |
Profile Node.js application | node --prof script.js | $ node --prof app.js |
Generate flamegraph from a V8 log file | node --prof-process --preprocess -j v8.log > output.svg | $ node --prof-process --preprocess -j v8.log > output.svg |
Part 3
Purpose | Syntax | Example |
---|---|---|
Update a globally installed package | npm update -g package-name | $ npm update -g express |
Install a package locally and save as a dev dependency | npm install package-name --save-dev | $ npm install jest --save-dev |
Execute scripts defined in package.json | npm run script-name | $ npm run test |
Display a list of available scripts | npm run | $ npm run |
Set the registry for npm | npm 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 account | npm adduser | $ npm adduser |
Check Node.js’s ability to reach the network | npm ping | $ npm ping |
List available npm scripts and their descriptions | npm scripts | $ npm scripts |
Show npm package details | npm show package-name | $ npm show lodash |
Check for npm registry connectivity issues | npm doctor | $ npm doctor |
Display npm’s help documentation | npm help | $ npm help |
Install packages with a specific node version | npm install package-name --engine-strict | $ npm install package-name --engine-strict |
List globally installed packages in a long format | npm ls -g --long | $ npm ls -g --long |
Install packages with a custom registry URL | npm install package-name --registry custom-registry-url | $ npm install lodash --registry https://my-registry.com |
Display the current npm configuration | npm config list | $ npm config list |
Show the package.json configuration | npm config list --json | $ npm config list --json |
Access the npm cache directory | npm config get cache | $ npm config get cache |
Verify the integrity of cached packages | npm cache verify | $ npm cache verify |
Clean the npm cache | npm cache clean | $ npm cache clean |
List installed global packages | npm ls -g --depth=0 | $ npm ls -g --depth=0 |
View package documentation in a web browser | npm docs package-name | $ npm docs express |
View the npm audit report for a package | npm audit | $ npm audit |
Run npm audit fix to automatically fix issues | npm 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:
Property | Purpose |
---|---|
name | Specifies the name of the package or application. |
version | Indicates the version of the package. |
description | Provides a brief description of the package. |
keywords | Lists keywords to help with package discovery. |
repository | Specifies the source code repository URL. |
author | Specifies the name and contact details of the author. |
license | Defines the package’s license type. |
dependencies | Lists production dependencies required for the app. |
devDependencies | Lists development dependencies required for testing. |
scripts | Defines custom scripts that can be run with npm . |
main | Specifies the entry point JavaScript file. |
bin | Maps package commands to executable scripts. |
engines | Specifies Node.js and npm version requirements. |
private | Marks the package as private (not for publication). |
homepage | Provides a URL to the project’s homepage. |
bugs | Points to the issue tracker for bug reporting. |
contributors | Lists contributors to the project. |
scripts.start | Defines the command to start the application. |
scripts.test | Defines the command to run tests. |
scripts.build | Specifies the build command for the project. |
scripts.prepublish | A script that runs before publishing the package. |
scripts.clean | Defines a custom clean-up script. |
scripts.lint | Defines a linting script for code quality checks. |
scripts.deploy | Specifies 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.