What is NodeJS?
NodeJS is an open-source, cross-platform, JavaScript runtime environment.
You can run JavaScript outside of the browser.
JavaScript can also talk to native machine because of C++.
You can create web servers in JavaScript Language.
Installation of NodeJS
Go to Node.js and download the Long-term support (LTS) version.
Check your Node version in terminal using
node -v
ornode --version
Check you NPM version in terminal using
npm -v
ornpm --version
Node Package Manager (NPM) is automatically install with Node.js
Use REPL
REPL stands for Read Evaluate Print Loop.
In your terminal execute
node
command to use REPL.
First code in NodeJS
Create a file: fileName.js
in your code editor like VS Code.
console.log("Hello! This is my first Node.js code");
Execute the above code in your own terminal. Run node fileName.js
or node fileName
and your code will executed inside your terminal (outside the browser).
Note:window
objects and DOM
features are excluded from Node, but file system
, HTTP Module
, Asynchronous
and Event-Driven
.
When creating a new project, we should initialize npm by running the npm init
command. This command creates a file called package.json
, which is a configuration file.
In package.json
, we should create our own scripts inside the scripts object for executing code. Scripts like 'start': node fileName
.
Modules in NodeJS
Module in Node.js is a simple or complex functionality organized in single or multiple JavaScript files which can be reused throughout the Node.js application.
Modular programming is a method for designing software that divides the program into smaller, self-contained parts called modules. Each module handles a specific task or feature, making it easier to manage and reuse code.
Create math.js
function add(a,b) {
return a+b;
}
function sub(a,b) {
return a-b;
}
console.log(add(2,5)); // => 7
module.exports = add;
module.exports = "Mark"; // It overrides the export.
// To get rid of the override, you should use a JavaScript object.
module.exports = {addFn:add, subFn:sub};
OR we use anonymous function using exports.propertyName
exports.add = (a,b) => a+b;
exports.sub = (a,b) => a-b;
Use require
similar to import
to import one module from another module.
Create index.js
const math = require('./math.js')
// OR destructures the function from math.js
const { addFn, subFn } = require('./math.js')
console.log("Math value is " + math) // => Math value is Mark
console.log(math.addFn(3,5)) // => 8
console.log(math.subFn(10,5)) // => 5
Note:./
means current directory.
File Handling in NodeJS
Reading and Writing the file in Node.js is done by using one of the built-in Node.js modules called fs module.
The Node.js file system module allows you to work with the file system on your computer.
The file can be read and written in node.js in both Synchronous and Asynchronous ways.
Synchronous methods block the code execution until they finish, while Asynchronous methods allow the code to continue running while they complete their tasks. Asynchronous methods often use callbacks or return promises to handle completion or errors.
Import fs
module
const fs = require('fs');
Common use for the File System module:
Create files
Read files
Update files
Delete files
Rename files
Create files
The methods for creating new files:
fs.writeFileSync()
fs.writeFileSync('./textSync.txt', 'Hello World Sync');
fs.writeFile()
fs.writeFile('./textAsync.txt', 'Hello World Async', (error) => { if(error) throw err; console.log('Saved!'); });
fs.appendFileSync()
fs.appendFileSync('./textSync.txt', `\nYou access at ${Date.now()}`)
fs.appendFile()
fs.appendFile('./textAsync.txt', `\nYou access at ${Date.now()}`, (error) => { error ? console.log("Error", err) : console.log('Saved!'); })
fs.open()
// File path const filePath = 'newfile.txt'; // Data to write to the file const data = 'Hello, this is a new file created using Node.js!'; // Open the file in write mode ('w') fs.open(filePath, 'w', (err, fileDescriptor) => { if (err) { console.error('Error occurred while opening the file:', err); return; } // Write data to the file fs.write(fileDescriptor, data, (err) => { if (err) { console.error('Error occurred while writing to the file:', err); return; } console.log('Data has been written to the file successfully.'); // Close the file fs.close(fileDescriptor, (err) => { if (err) { console.error('Error occurred while closing the file:', err); return; } console.log('File has been closed.'); }); }); });
fileDescriptor
: is the variable that holds the unique ID number (file descriptor) given to the file by the computer. This number is used by your program to work with the file.
Read files
The methods for reading files:
fs.readFileSync()
const result = fs.readFileSync('./textSync.txt', 'utf-8'); console.log(result);
fs.readFile()
fs.readFile('./textAsync.txt', 'utf-8', (error, result) => { error ? console.log("Error: ", error) : console.log(result); })
Update files
fs.append()
fs.appendFile('./textAsync.txt', `\nYou access at ${Date.now()}`, (error) => { error ? console.log("Error", err) : console.log('Updated!'); })
fs.writeFile()
fs.writeFile('./textAsync.txt', 'Hello World Async', (error) => { if(error) throw err; console.log('Replaced!'); });
Delete files
fs.unlink()
fs.unlink('./newfile.txt', (error) => { if(error) throw error; console.log('File deleted!') })
fs.unlinkSync()
fs.unlinkSync('./newfile.txt')
Rename files
fs.rename()
fs.rename('myfile.txt', 'myrenamedfile.txt', function (err) { if (err) throw err; console.log('File Renamed!'); });
Other operations:
fs.cp()
fs.cp('./test.txt', './copy.txt', (error) => { if(error) throw error; console.log('File copied!') });
fs.cpSync()
fs.cpSync('./text.txt', './copy.txt')
fs.stat()
fs.stat('./test.txt', (err, stats) => { if (err) { console.error(err); } console.log(stats); });
fs.statSync()
console.log(fs.statSync('./test.txt'))
fs.mkdirSync()
fs.mkdirSync('DSA/Array/Easy', {recursive:true})
fs.mkdir()
const path = require('path'); fs.mkdir(path.join(__dirname, 'templates'), error => { if(error) console.log(error); console.log('Folder was created successfully!') })
NodeJS Architecture
What is NodeJS Architecture?
Node.js architecture consists of several key components that work together to provide an efficient and scalable runtime environment for JavaScript applications.
V8 Engine:
Node.js is built on top of the V8 JavaScript engine, which is developed by Google and also used in the Chrome web browser.
V8 is responsible for executing JavaScript code and provides features such as Just-In-Time (JIT) compilation, garbage collection, and optimized performance.
Libuv:
Libuv is a cross-platform library that provides asynchronous I/O capabilities, event loop implementation, and other core functionalities for Node.js.
It abstracts away the differences in I/O operations between different operating systems, allowing Node.js to be highly portable and efficient.
Libuv handles tasks such as file system operations, networking, timers, and threading, making Node.js suitable for building scalable network applications.
Event Loop:
The event loop is a fundamental concept in Node.js architecture that enables non-blocking, asynchronous behavior.
It allows Node.js to handle multiple I/O operations concurrently without blocking the execution of other code.
The event loop continuously checks for events (such as I/O operations, timers, and callbacks) in the event queue and processes them in a single-threaded manner.
Asynchronous operations in Node.js are achieved through the event loop and callback functions.
Core Modules:
Node.js provides a set of built-in core modules that offer essential functionalities for building applications.
Core modules include modules for file system operations (
fs
), networking (http
,https
,net
), path manipulation (path
), and more.These modules are implemented in C/C++ for performance and are exposed to JavaScript through the Node.js runtime environment.
Node.js APIs:
Node.js provides additional APIs for interacting with the runtime environment, accessing operating system functionalities, and extending the capabilities of JavaScript applications.
APIs include the
process
object for accessing information about the current Node.js process, theos
module for operating system-related tasks, and theutil
module for utility functions.
Third-Party Modules and NPM:
Node.js has a vibrant ecosystem of third-party modules available through the Node Package Manager (NPM).
Developers can easily install and use third-party modules to extend the functionality of their Node.js applications, making it easy to integrate with databases, web frameworks, authentication systems, and more.
Overall, Node.js architecture is designed to be efficient, scalable, and suitable for building high-performance network applications that can handle a large number of concurrent connections. Read this article gain a better understanding.
What is blocking and non-blocking?
Blocking Operations | Non-Blocking Operations |
Blocking operations are synchronous, meaning the program execution waits until the operation completes before moving on to the next instruction. | Non-blocking operations are asynchronous, meaning the program execution continues immediately after the operation is initiated without waiting for it to complete. |
During a blocking operation, the entire program execution is paused until the operation finishes. | During a non-blocking operation, the program doesn't wait for the operation to finish and can continue executing other tasks. |
If there are multiple blocking operations in sequence, each operation will be executed one after the other, blocking the program's execution until all operations are complete. | Non-blocking operations are well-suited for I/O-bound tasks where waiting for I/O operations to complete would result in wasted CPU cycles and decreased performance. |
Blocking operations are typically used in traditional programming environments. | Non-blocking operations are a key feature of Node.js, allowing it to efficiently handle multiple I/O operations concurrently without blocking the event loop. |
What is a difference between Sync and Async?
Synchronous (Sync) Operations | Asynchronous (Async) Operations |
Synchronous operations execute in a sequential manner, one after the other. | Asynchronous operations do not block the program's execution. Instead, they allow the program to continue executing while waiting for the operation to complete. |
When a synchronous operation is called, the program waits for it to complete before moving on to the next line of code. | When an asynchronous operation is called, it is initiated, and the program moves on to the next line of code without waiting for the operation to finish. |
Synchronous operations block the execution of the program until they finish. | Asynchronous operations typically accept a callback function that gets executed once the operation completes or encounters an error. |
Example: fs.readFileSync() in Node.js, which reads a file synchronously. During the file read operation, the program is blocked until the entire file is read. | Example: fs.readFile() in Node.js, which reads a file asynchronously. After initiating the file read operation, the program continues executing while the file is being read. Once the file read is complete, the callback function is called. |
Which one should I use?
It depends on the specific use case and requirements of your application.
Synchronous operations are simpler to understand and use when dealing with simple sequences of actions. However, they can block the event loop and lead to decreased performance, especially in I/O-bound applications.
Asynchronous operations are more efficient and scalable, particularly in I/O-bound scenarios where waiting for I/O operations to complete would result in wasted CPU cycles. They allow Node.js to handle multiple operations concurrently without blocking the event loop.
In general, it's recommended to use asynchronous operations in Node.js applications, especially for I/O-bound tasks like file I/O, network requests, and database queries, to ensure optimal performance and scalability. However, synchronous operations may be suitable for simpler, CPU-bound tasks or situations where simplicity and sequential execution are more important than performance.
Blocking (Sync) Code
const fs = require('fs');
console.log('Hello World');
const result = fs.readFileSync('./textSync.txt', 'utf-8');
console.log(result);
console.log('Bye World');
Non-Blocking (Async) Code
const fs = require('fs');
console.log('Hello World');
fs.readFile('./textAsync.txt', 'utf-8', (err, result) => {
err ? console.log('Error', err) : console.log(result);
})
console.log('Bye World');
Note: The default thread pool size is 4. The maximum size depends on the machine; for example, a machine with an 8-core CPU can have a maximum thread pool size of 8.
HTTP Server in NodeJS
Build a new project
Run the following command in your terminal:
npm init -y
will simply generate an empty npm project and create thepackage.json
file.-y
stands for yes,-y
flag when passed to NPM commands tells the generator to use the defaults.Creating an
index.js
file is a good practice for setting up your entry point.const http = require('http'); const myServer = http.createServer((req, res) => { console.log("New Request Received"); console.log(req); // returns big object which contain the information of user. console.log(req.headers); // gives extra information about user. res.end("Hello From Server"); }) // To run this server we need a PORT. myServer.listen(8000, () => console.log('Server Started!'))
Go to the
package.json
and change thescripts
command.{ "name": "myserver", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index" }, "author": "", "license": "ISC", "dependencies": { "url": "^0.11.3" } }
To run the
index.js
file, we should execute the commandnpm start
.
Assignment
Write a code which store users log inside
log.txt
.const http = require('http'); const fs = require('fs'); const myServer = http.createServer((req, res) => { const log = `${Date.now()}: ${req.url}: New Req Received\n`; fs.appendFile('./log.txt', log, (err, data) => { switch(req.url) { case '/': res.end("HomePage"); break; case '/about': res.end("I am Mark"); break; default: res.end("404 Not Found"); } }); }); myServer.listen(8000, () => console.log("Server Started!"))
log.txt
file looks like:1711101736526: /: New Req Received 1711101736811: /favicon.ico: New Req Received 1711102053265: /about: New Req Received 1711102053375: /favicon.ico: New Req Received 1711102062264: /contact: New Req Received 1711102062358: /favicon.ico: New Req Received
URL's in NodeJS
Example URL: https://www.heymark.vercel.app/
Protocol:
https://
(Hypertext Transfer Protocol Secure)Domain:
www.heymark.vercel.app
(User Friendly Name of IP Address of My Server)Path:
/
(Root Path)Paths:
/about
,/contact-us
Nested Path:
/project/github-profile-search
Query Parameters:
https://www.heymark.vercel.app/?urerId=1&a=2
(Query parameter is key-value pair start with ?)?urerId=1&a=2
Note:
q
means query,+
is used for spaces,&
is used as a delimiter to separate multiple query parameters.Examples:
https://www.google.com/search?q=javascript&oq=javascript&gs_lcrp=EgZjaHJvbWUyDwgAEEUYORiDARixAxiABDIGCAEQRRg8MgYIAhBFGD0yBggDEEUYPDIGCAQQRRg8MgYIBRBFGEEyBggGEEUYQTIGCAcQRRhB0gEIMzUwM2owajeoAgCwAgA&sourceid=chrome&ie=UTF-8
Protocol:
https://
Domain:
www.google.com
Path:
/search
Query Parameters:
q=javascript&oq=javascript&gs_lcrp=EgZjaHJvbWUyDwgAEEUYORiDARixAxiABDIGCAEQRRg8MgYIAhBFGD0yBggDEEUYPDIGCAQQRRg8MgYIBRBFGEEyBggGEEUYQTIGCAcQRRhB0gEIMzUwM2owajeoAgCwAgA&sourceid=chrome&ie=UTF-8
The res.url
property provides the path of the URL using the http
module. The http
module does not understand query parameters. To handle query parameters, install the url
module using npm install url
.
index.js
file:
const http = require('http');
const fs = require('fs');
const url = require('url');
const myServer = http.createServer((req, res) => {
if(req.url === '/favicon.ico') return res.end();
const log = `${Date.now()}: ${req.url} New Req Received\n`;
const myUrl = url.parse(req.url);
console.log(myUrl);
fs.appendFile('log.txt', log, (err, data) => {
switch (req.url) {
case '/':
res.end('Home Page');
break;
case '/about':
res.end('I am Mark');
break;
default:
res.end('404 Not Found');
}
});
});
myServer.listen(8000, () => console.log('Server Started!'))
URL:http://localhost:8000/about?myname=John&userid=1&search=javascript
const http = require('http');
const fs = require('fs');
const url = require('url');
const myServer = http.createServer((req, res) => {
if(req.url === '/favicon.ico') return res.end();
const log = `${Date.now()}: ${req.url} New Req Received\n`;
const myUrl = url.parse(req.url, true);
console.log(myUrl);
fs.appendFile('log.txt', log, (err, data) => {
switch (myUrl.pathname) {
case '/':
res.end('Home Page');
break;
case '/about':
const username = myUrl.query.myname
res.end(`Hey ${username}`);
break;
case '/search':
const search = myUrl.query.search_query;
res.end('Here are your results for ' + search)
default:
res.end('404 Not Found');
}
});
});
myServer.listen(8000, () => console.log('Server Started!'))
URL:http://localhost:8000/about?myname=John&userid=1&search=javascript
URL:http://localhost:8000/search?search_query=javascript+amazon+clone
HTTP Methods in NodeJS
GET: Used to request data from a specified resource. It retrieves data from the server based on the parameters sent in the URL.
POST: Used to submit data to be processed to a specified resource. It sends data to the server in the body of the HTTP request to create or update a resource.
PUT: Used to update existing data on the server. It sends data to a specific resource to update it or create it if it doesn't exist.
PATCH: Used to partially update existing data on the server. It sends data to modify specific fields of an existing resource.
DELETE: Used to delete existing data from the server. It sends a request to remove a specific resource.
index.js
file.
const http = require('http');
const fs = require('fs');
const url = require('url');
const myServer = http.createServer((req, res) => {
if(req.url === '/favicon.ico') return res.end();
const log = `${Date.now()}: ${req.method} ${req.url} New Req Received\n`;
const myUrl = url.parse(req.url, true);
fs.appendFile('log.txt', log, (err, data) => {
switch (myUrl.pathname) {
case '/':
if(req.method === 'GET') res.end("Home Page")
break;
case '/about':
const username = myUrl.query.myname
res.end(`Hey ${username}`);
break;
case '/search':
const search = myUrl.query.search_query;
res.end('Here are your results for ' + search)
case '/signup':
if(req.method === 'GET') res.end("This is a signup form");
else if(req.method === 'POST') {
// DB Query
res.end("Success");
}
default:
res.end('404 Not Found');
}
});
});
myServer.listen(8000, () => console.log('Server Started!'))
URL's: htts://localhost:8000/
, http:/localhost:8000/about
, http://localhost:8000/about?myname=Mark
log.txt
file.
1711105800452: GET / New Req Received
1711105827923: GET /about New Req Received
1711105859340: GET /about?myname=Mark New Req Received
Master NodeJS withPiyush Garg