Get Started
The usual way to run a Node.js program is by executing it using the globally available node command. You can pass the name of the file you want to execute as an argument.
node app.js
Here, you explicitly specify the node interpreter to run your script. However, you can also embed this information into your JavaScript file with a “shebang” line.
A shebang line is the first line of a file and tells the operating system which interpreter to use for running the script. You can use two different approaches for specifying the shebang line.
Explicitly specifying the interpreter’s path:
#!/usr/bin/node
In this approach, you explicitly give the absolute path of the node interpreter. Note that not all operating systems may have node in the /usr/bin folder.
Using ‘env’ to locate the interpreter:
#!/usr/bin/env node
Here, you use the env command to locate the node interpreter. This approach is more portable because it relies on the assumption that the env command is available on most operating systems.
To use a shebang, your JavaScript file should have executable permission. You can grant executable permission to a file using the chmod command:
chmod u+x app.js
Make sure you are in the same directory as the app.js file when running this command.
Passing a string as an argument to node
You can execute a JavaScript string as an argument to the node command using the -e or --eval option:
node -e "console.log(123)"
This is useful for quick one-liners or for evaluating JavaScript code from the command line. Note that the usage of single or double quotes can vary depending on your shell (e.g., cmd.exe, PowerShell, or Git Bash on Windows).
Restarting the application automatically with Nodemon
To automatically restart your Node.js application whenever there is a change, you can use the nodemon module. First, install nodemon globally:
npm i -g nodemon
You can also install nodemon as a development dependency:
npm i --save-dev nodemon
After installation, you can run nodemon from an npm script, such as npm start, or directly using npx nodemon. For example:
nodemon app.js
This will monitor your application’s files for changes and automatically restart it when necessary, making development more convenient.
env
Node.js provides a straightforward way to work with environment variables, which are essential for configuring applications. In this guide, we’ll explore how to access and manage environment variables in a Node.js project.
Accessing Environment Variables
Node.js exposes environment variables through the process.env object. These variables contain information about the system environment at the time the Node.js process was started.
To set environment variables for a specific Node.js process, you can use the command line as follows:
USER_ID=239482 USER_KEY=foobar node app.js
Here, we set the USER_ID to 239482 and USER_KEY to foobar.
Accessing Environment Variables in Your Code
Once you’ve set environment variables, you can easily access them in your Node.js code using process.env. For example:
const userId = process.env.USER_ID; // "239482"
const userKey = process.env.USER_KEY; // "foobar"
You can access any custom environment variables in the same way.
Using an .env File
Managing multiple environment variables directly in the command line can become cumbersome. To simplify this process, you can use an .env file in your project’s root directory and the dotenv package.
Installing dotenv
First, install the dotenv package locally in your project, which is recommended:
npm install dotenv --save
Creating the .env File
Create a .env file in your project’s root directory. Here’s an example:
USER_ID="239482"
USER_KEY="foobar"
NODE_ENV="development"
Loading Environment Variables
In your JavaScript file, require and configure dotenv to load the environment variables from the .env file:
require('dotenv').config();
const userId = process.env.USER_ID; // "239482"
const userKey = process.env.USER_KEY; // "foobar"
const nodeEnv = process.env.NODE_ENV; // "development"
By using dotenv, you can manage and load environment variables more efficiently. If you prefer not to import the package in your code, you can run your JavaScript file with the following command:
node -r dotenv/config index.js
This command loads the environment variables from the .env file without the need for explicit imports in your code.
HTTP Module
In Node.js, you can make HTTP requests using various methods and libraries. Here, we’ll explore how to perform GET, POST, PUT, and DELETE requests, starting with GET and POST.
GET Request
Using Axios Library
To make a GET request using Axios, follow these steps:
- Install Axios using npm:
$ npm install axios - Import Axios in your script:
const axios = require('axios'); - Make the GET request:
axios .get('https://example.com/todos') .then(res => { console.log(`statusCode: ${res.status}`); console.log(res.data); }) .catch(error => { console.error(error); });
Using Node.js Standard Modules
You can also perform a GET request using Node.js standard modules. While it’s more verbose, it doesn’t require a third-party library:
const https = require('https');
const options = {
hostname: 'example.com',
port: 443,
path: '/todos',
method: 'GET',
};
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.end();
POST Request
Using Axios Library
To make a POST request using Axios:
const axios = require('axios');
const data = {
todo: 'Buy the milk',
};
axios
.post('https://whatever.com/todos', data)
.then(res => {
console.log(`statusCode: ${res.status}`);
console.log(res.data);
})
.catch(error => {
console.error(error);
});
Using Node.js Standard Modules
For a POST request with Node.js standard modules:
const https = require('https');
const data = JSON.stringify({
todo: 'Buy the milk',
});
const options = {
hostname: 'whatever.com',
port: 443,
path: '/todos',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
};
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.write(data);
req.end();
PUT and DELETE Requests
PUT and DELETE requests follow a similar pattern to POST requests. To perform these requests, adjust the options.method value accordingly.
File System
The fs module in Node.js provides a comprehensive set of functionalities to interact with the file system. It is a core module of Node.js and does not require any additional installation.
To use the fs module, you can simply require it like this:
const fs = require('fs');
Once you’ve required it, you gain access to a wide range of methods for various file system operations. Here’s an overview of some of the most commonly used methods:
Common fs Methods
-
fs.access(): Checks if a file exists and if Node.js can access it with the specified permissions. -
fs.appendFile(): Appends data to a file. If the file does not exist, it will be created. -
fs.chmod(): Changes the permissions of a file specified by the filename passed. Also related methods:fs.lchmod(),fs.fchmod(). -
fs.chown(): Changes the owner and group of a file specified by the filename passed. Also related methods:fs.fchown(),fs.lchown(). -
fs.close(): Closes a file descriptor. -
fs.copyFile(): Copies a file. -
fs.createReadStream(): Creates a readable file stream. -
fs.createWriteStream(): Creates a writable file stream. -
fs.link(): Creates a new hard link to a file. -
fs.mkdir(): Creates a new folder. -
fs.mkdtemp(): Creates a temporary directory. -
fs.open(): Opens a file and returns a file descriptor for file manipulation. -
fs.readdir(): Reads the contents of a directory. -
fs.readFile(): Reads the content of a file. Related method:fs.read(). -
fs.readlink(): Reads the value of a symbolic link. -
fs.realpath(): Resolves relative file path pointers (e.g.,.and..) to the full path. -
fs.rename(): Renames a file or folder. -
fs.rmdir(): Removes a folder. -
fs.stat(): Returns the status of the file identified by the filename passed. Related methods:fs.fstat(),fs.lstat(). -
fs.symlink(): Creates a new symbolic link to a file. -
fs.truncate(): Truncates a file to the specified length. Related method:fs.ftruncate(). -
fs.unlink(): Removes a file or a symbolic link. -
fs.unwatchFile(): Stops watching for changes on a file. -
fs.utimes(): Changes the timestamp of the file identified by the filename passed. Related method:fs.futimes(). -
fs.watchFile(): Starts watching for changes on a file. Related method:fs.watch(). -
fs.writeFile(): Writes data to a file. Related method:fs.write().
Asynchronous and Synchronous Methods
One notable aspect of the fs module is that all its methods are asynchronous by default. However, they can also work synchronously by appending Sync to the method name.
For example, asynchronous and synchronous versions of fs.rename() and fs.write() exist:
- Asynchronous:
fs.rename(),fs.write() - Synchronous:
fs.renameSync(),fs.writeSync()
Using synchronous methods can block the execution of your script until the file operation is completed.
Promise-based API (Node.js 10+)
Starting from Node.js 10, there is experimental support for a promise-based API in the fs/promises module. This can help avoid callback hell when dealing with nested asynchronous operations. Here’s an example of using the promise-based API:
const fs = require('fs/promises');
async function example() {
const fileName = '/Users/joe/test.txt';
try {
const data = await fs.readFile(fileName, 'utf8');
console.log(data);
const content = 'Some content!';
await fs.writeFile(fileName, content);
console.log('Wrote some content!');
const newData = await fs.readFile(fileName, 'utf8');
console.log(newData);
} catch (err) {
console.log(err);
}
}
example();
By using the promise-based API, you can write more readable and maintainable code, especially when dealing with complex file system operations.
File Writing in Node.js
File manipulation is a common task in Node.js, and it can be achieved using various methods. Here, we’ll explore different approaches to write to files in Node.js, along with additional details to help you understand the process better.
Using fs.writeFile()
The simplest way to write to a file in Node.js is to use the fs.writeFile() method. This method asynchronously writes data to a file. If the file already exists, it will be overwritten.
const fs = require('fs');
const content = 'Some content!';
fs.writeFile('/Users/joe/test.txt', content, err => {
if (err) {
console.error(err);
} else {
// File written successfully
}
});
Using fs.writeFileSync()
If you prefer a synchronous approach, you can use fs.writeFileSync(). This method writes data to a file synchronously, blocking the execution until the write operation is complete. It is important to handle potential errors.
const fs = require('fs');
const content = 'Some content!';
try {
fs.writeFileSync('/Users/joe/test.txt', content);
// File written successfully
} catch (err) {
console.error(err);
}
Using fsPromises.writeFile()
For a more modern and asynchronous approach with Promises, you can use fsPromises.writeFile(), available in the fs/promises module.
const fs = require('fs/promises');
async function example() {
try {
const content = 'Some content!';
await fs.writeFile('/Users/joe/test.txt', content);
// File written successfully
} catch (err) {
console.log(err);
}
}
example();
Modifying Write Behavior
You can modify the default behavior of file writing by specifying a flag when using fs.writeFile() or fsPromises.writeFile(). Common flags include:
r+: Open the file for reading and writing.w+: Open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if it does not exist.a: Open the file for writing, positioning the stream at the end of the file. The file is created if it does not exist.a+: Open the file for reading and writing, positioning the stream at the end of the file. The file is created if it does not exist.
Example:
fs.writeFile('/Users/joe/test.txt', content, { flag: 'a+' }, err => {});
You can find more flags in the Node.js documentation.
Appending to a File
To append content to the end of a file, you can use fs.appendFile() (asynchronous) or fs.appendFileSync() (synchronous).
Example using fs.appendFile():
const fs = require('fs');
const content = 'Some content!';
fs.appendFile('file.log', content, err => {
if (err) {
console.error(err);
} else {
// Append operation completed successfully
}
});
Example using fsPromises.appendFile():
const fs = require('fs/promises');
async function example() {
try {
const content = 'Some content!';
await fs.appendFile('/Users/joe/test.txt', content);
// Append operation completed successfully
} catch (err) {
console.log(err);
}
}
example();
Using Streams
While the methods mentioned above write the entire content to the file at once, a more efficient option for larger files is to use streams. Streams allow you to write data in smaller chunks, reducing memory usage and improving performance. Stream-based file writing is especially useful for handling large files.
Reading Files in Node.js
In Node.js, you can read files using different methods depending on your needs. Here are three common ways to read a file, along with some additional details to help you understand the process better:
Using fs.readFile()
The simplest way to read a file is by using the fs.readFile() method. This asynchronous method reads the entire file into memory and requires a callback function to handle the data and any potential errors.
const fs = require('fs');
fs.readFile('/Users/joe/test.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
fs.readFile()is asynchronous, meaning it won’t block the execution of other code while reading the file.- The
'utf8'encoding specifies that the file should be read as a UTF-8 encoded text file.
Using fs.readFileSync()
If you prefer a synchronous approach, you can use fs.readFileSync(). This method reads the entire file synchronously and throws an error if any issues occur.
const fs = require('fs');
try {
const data = fs.readFileSync('/Users/joe/test.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
fs.readFileSync()blocks the execution of the program until the file is read completely.- It’s important to handle potential errors using a
try...catchblock.
Using fsPromises.readFile()
Node.js also offers a promise-based way to read files using fsPromises.readFile() from the fs/promises module. This approach allows you to work with promises and async/await.
const fs = require('fs/promises');
async function example() {
try {
const data = await fs.readFile('/Users/joe/test.txt', { encoding: 'utf8' });
console.log(data);
} catch (err) {
console.error(err);
}
}
example();
fsPromises.readFile()provides a more modern and cleaner syntax for handling asynchronous operations usingasync/await.- The
{ encoding: 'utf8' }option specifies the file’s encoding.
Memory Considerations
It’s essential to be mindful of memory consumption and execution speed, especially when dealing with large files. All three methods mentioned above read the entire file into memory, which can impact performance and memory usage.
For better efficiency when working with large files, consider using file streams, a mechanism that reads and processes files in smaller chunks, reducing memory consumption and improving performance. Streams are especially useful when dealing with large files or when you need to process data incrementally.
In summary, choose the appropriate file reading method based on your application’s requirements and consider using file streams for better performance and memory management when handling large files.
URL Class
The Browser-compatible URL class is implemented following the WHATWG URL Standard. This class is used for parsing URLs in accordance with browser conventions. It is also available on the global object.
- All properties of URL objects are implemented as getters and setters on the class prototype, not as data properties on the object itself. Therefore, using the
deletekeyword on any properties of URL objects has no effect but will still returntrue.
new URL(input[, base])
input<string>: The absolute or relative input URL to parse. Ifinputis relative,baseis required. Ifinputis absolute,baseis ignored. Ifinputis not a string, it is converted to a string first.base<string>: The base URL to resolve against ifinputis not absolute. Ifbaseis not a string, it is converted to a string first.
Creates a new URL object by parsing the input relative to the base. If base is passed as a string, it will be parsed equivalently to new URL(base).
Example:
const myURL = new URL('/foo', 'https://example.org/');
// Result: https://example.org/foo
- The URL constructor is accessible as a property on the global object and can also be imported from the built-in
urlmodule (in Node.js).
console.log(URL === require('node:url').URL); // Prints 'true'.
- A
TypeErrorwill be thrown if theinputorbaseare not valid URLs. Efforts are made to coerce the given values into strings.
Example:
const myURL = new URL({ toString: () => 'https://example.org/' });
// Result: https://example.org/
- Unicode characters within the host name of
inputwill be automatically converted to ASCII using the Punycode algorithm.
Example:
const myURL = new URL('https://測試');
// Result: https://xn--g6w251d/
- When it is not known in advance if
inputis an absolute URL and abaseis provided, it is advised to validate that the origin of the URL object is as expected.
Examples:
let myURL = new URL('http://Example.com/', 'https://example.org/');
// Result: http://example.com/
myURL = new URL('https://Example.com/', 'https://example.org/');
// Result: https://example.com/
myURL = new URL('foo://Example.com/', 'https://example.org/');
// Result: foo://Example.com/
myURL = new URL('http:Example.com/', 'https://example.org/');
// Result: http://example.com/
myURL = new URL('https:Example.com/', 'https://example.org/');
// Result: https://example.org/Example.com/
myURL = new URL('foo:Example.com/', 'https://example.org/');
// Result: foo:Example.com/
All the properties a re avaliable in the Node.js documentation.
NPM
npm is the standard package manager for Node.js. As of September 2022, it boasts over 2.1 million packages in its registry, making it the largest single-language code repository globally. With npm, you can find a package for almost any purpose, making it an invaluable tool for Node.js development. While its initial purpose was to manage dependencies for Node.js packages, it has since become a vital tool in frontend JavaScript development as well.
npm Alternatives
- Yarn: A package manager that focuses on speed and reliability, often used as an alternative to npm.
- pnpm: A fast and disk-space-efficient package manager compatible with npm, designed to save disk space and speed up installations.
Managing Downloads
npm excels at managing the downloads of project dependencies.
Installing All Dependencies
To install all dependencies listed in a project’s package.json file, simply run the following command:
npm install
This command will create a node_modules folder if it doesn’t exist and install all the required packages.
Installing a Single Package
For installing a specific package, use the following command:
npm install <package-name>
Starting from npm version 5, the <package-name> is automatically added to the dependencies section of the package.json file. In earlier versions, you needed to add the --save flag. Additional flags can be used to modify this behavior:
--save-dev: Installs the package and adds it to thedevDependenciessection ofpackage.json.--no-save: Installs the package but doesn’t add it topackage.json.--save-optional: Installs the package and adds it tooptionalDependenciesinpackage.json.--no-optional: Prevents the installation of optional dependencies.
Shorthand versions of these flags are also available:
-S: Equivalent to--save-D: Equivalent to--save-dev-O: Equivalent to--save-optional
Note: The distinction between devDependencies and dependencies is that the former includes development tools, such as testing libraries, while the latter is bundled with the application in production. Optional dependencies do not block installation if they fail to build, but handling their absence is the responsibility of your program.
Updating Packages
Updating packages is straightforward with npm. To check for newer versions of packages that meet your versioning constraints, use:
npm update
To update a specific package, use:
npm update <package-name>
Versioning
npm not only manages package downloads but also handles versioning. You can specify precise versions or define version ranges to ensure compatibility with your project.
Sometimes, you may find that a library is only compatible with a specific major release of another library, or a bug in the latest release of a library remains unfixed. Specifying explicit versions helps maintain consistency within your team until you update the package.json file.
npm follows the semantic versioning (semver) standard, which helps in version management.
To install a specific version of a package, use the following command:
npm install <package-name>@<version>
Running Tasks
The package.json file supports defining command-line tasks that can be executed using npm run <task-name>.
For example, you can define tasks like this:
{
"scripts": {
"start-dev": "node lib/server-development",
"start": "node lib/server-production"
}
}
This feature is commonly used to run tools like Webpack:
{
"scripts": {
"watch": "webpack --watch --progress --colors --config webpack.conf.js",
"dev": "webpack --progress --colors --config webpack.conf.js",
"prod": "NODE_ENV=production webpack -p --config webpack.conf.js"
}
}
Instead of typing long and potentially error-prone commands, you can use these defined tasks:
$ npm run watch
$ npm run dev
$ npm run prod
These tasks simplify the execution of complex commands in your development workflow.
package.json
package.json has detailed documentation of its properties.
Local and Global Packages
When working with Node.js and npm (Node Package Manager), it’s essential to distinguish between local and global packages. The primary difference lies in how and where these packages are installed and accessed within your development environment.
Local Packages
Local packages are installed in the directory where you execute the command npm install <package-name>. Here are some key points to remember:
- Installation Location: They are placed within the
node_modulesdirectory under the specific project directory where thenpm installcommand was run. - Access in Code: To use local packages in your code, you typically require them using
require('package-name').
When to Install Packages Locally
In general, the best practice is to install packages locally for the following reasons:
-
Isolation: Each project maintains its own set of local packages, ensuring that different projects can use different versions of the same package without conflicts. This isolation is crucial to avoid compatibility issues and ensure project stability.
-
Version Control: Including local packages in your project’s version control system (e.g., Git) allows you to track and share the specific package versions used in your project, enhancing collaboration and reproducibility.
Global Packages
Global packages, on the other hand, are stored in a single location on your system, regardless of where you execute the npm install -g <package-name> command. Here are additional details:
- Installation Location: Global packages are installed in a system-wide directory, the exact location of which depends on your system’s configuration.
- Access in Code: You cannot directly require global packages in your code as you would with local packages. Instead, global packages typically provide executable commands that you can run from the command line (CLI).
When to Install Packages Globally
Install packages globally when they provide executable commands that you need to access from the shell (CLI), and you intend to use these commands across multiple projects. Some common examples of global packages include:
- npm
- vue-cli
- grunt-cli
- mocha
- react-native-cli
- gatsby-cli
- forever
- nodemon
You can check your system for globally installed packages by running the following command in your terminal:
npm list -g --depth 0
This command will list all global packages installed on your system, helping you identify packages with global CLI functionality.
Events
In Node.js, the events module allows you to handle and manage events, similar to how interactions with users are handled in JavaScript within the browser. The events module provides the EventEmitter class for managing events.
Importing the EventEmitter Class
To start working with events, you first need to import the EventEmitter class from the events module and create an instance of it:
const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
Basic Event Handling
-
emit(event, [args]): This method is used to trigger an event. You can also pass additional arguments to the event handler function.eventEmitter.emit('start'); // Trigger the 'start' event eventEmitter.emit('start', 23); // Pass an argument to the event handler eventEmitter.emit('start', 1, 100); // Pass multiple arguments to the event handler -
on(event, callback): Use this method to add a callback function that will be executed when the specified event is triggered.eventEmitter.on('start', () => { console.log('started'); });
Additional Event Handling Methods
The EventEmitter object provides various methods for handling events:
-
once(event, callback): Adds a one-time listener that will be executed only the first time the event is emitted. -
removeListener(event, callback)/off(event, callback): Removes a specific event listener for the given event. -
removeAllListeners([event]): Removes all listeners for a specific event or all events if no event name is provided. -
eventNames(): Returns an array of strings representing the events registered on the currentEventEmitterobject. -
getMaxListeners(): Retrieves the maximum number of listeners that can be added to anEventEmitterobject. -
listenerCount(event): Returns the count of listeners for the specified event. -
listeners(event): Gets an array of listeners for the specified event. -
prependListener(event, callback): Adds a listener to the beginning of the listeners queue for the specified event. -
prependOnceListener(event, callback): Adds a one-time listener to the beginning of the listeners queue for the specified event. -
setMaxListeners(n): Sets the maximum number of listeners that can be added to anEventEmitterobject. The default is 10 but can be increased or decreased.
In-Built Events
The EventEmitter object also has two in-built events:
newListener: Emitted when a new listener is added.removeListener: Emitted when a listener is removed.
Setting and Getting Maximum Listeners
You can set and get the maximum number of listeners for an EventEmitter object using the setMaxListeners(n) and getMaxListeners() methods. The default maximum is 10.
eventEmitter.setMaxListeners(50); // Increase the maximum number of listeners
const maxListeners = eventEmitter.getMaxListeners(); // Get the current maximum listeners limit
For more details on these methods and additional functionalities, refer to the official documentation of the events module.