NodeJS
| Created | |
|---|---|
| Type | Web |
| Language | Javascript |
| Last Edit |
Basics
Description
Is a JavaScript runtime built on Chrome’s V8 JavaScript engine.
Open source and cross-platform run-time env for executing javascript code outside of browser.
Used often for backend services.
How It Works
Has non-blocking async nature: Single thread will serve multiple requests when data is being fetched for another request
Blocking sync nature: multiple
thread for multiple requests since each thread waits for data to be fetched.
- Example: ASP.NET
NodeJS uses EventQueue for managing requests, data returns etc
Architecture
Node.exe is a C++ program which contains Google V8 javascript engine. It contains additional modules like
filesystem, network etc (not available in browsers)
Ideal For
i/o intensive apps: Excessive disk or network access
Installation
Installation of Node 14 on M1 using Rosetta
$ arch
# O/p: arm
# Switch to 64
$ arch -x86_64 zsh
$ arch
# O/p: i386
$ nvm install 14Module System
ESM
Include "type": "module", in package.json file.
FileSystem Module
Create file with content and append data to it:
const fs = require("fs");
//Write a file
fs.writeFileSync("notes.txt", "This file was created by Node.js!");
//append data to existing file
fs.appendFileSync("notes.txt", " New line appended by node");Working with JSON
Convert and store JSON data:
const fs = require("fs");
const data = {
author: "Emily",
title: "Ego is the Enemy",
};
fs.writeFileSync("1-json.json", JSON.stringify(data));Read and convert JSON data:
const fs = require("fs");
const dataBuffer = fs.readFileSync("1-json.json");
const dataJSON = dataBuffer.toString();
const data = JSON.parse(dataJSON);
console.log(data);
console.log(data.title);User Defined Modules
Following code can be used to import local files.
require("./utils.js");utils1.js
const username = "Username";
module.exports = username;utils2.js
const add = function(a,b) {
return a + b;
}
module.exports = addapp.js
const name = require("./utils1.js");
console.log(name);
const add = require("./utils2.js");
console.log(add(2,5));Multiple exports
module.exports = {
getNotes: getNotes,
addNote: addNote
};
//or
module.exports = { getNotes, addNote, removeNote };const notes = require("./notes.js");
notes.getNotes();
notes.addNote(title,body);NPM
Running following command in terminal to setup package.json to use npm packages.
npm initAfter installing a particular package using npm i, it can be imported as follows:
const validator = require("validator");
console.log(validator.isEmail("example.com"));Chalk
NPM package used to stylise console message.
const chalk = require("chalk");
const error = chalk.bgRed;
const success = chalk.bold.green; // or chalk.green.bold
const warning = chalk.hex("#FFA500"); // Orange color
console.log(success("Success!"));
console.log(warning("Warning!"));
console.log(error("Error!"));Nodemon
NPM package used to hot reload on save.
nodemon app.js"scripts": {
"start": "",
"dev": "nodemon src/index.js"
},npm run devYargs
Package used to parse command line arguments
Yargs Setup
const yargs = require("yargs");
// Customize yargs version
yargs.version("1.1.0");
// Setup commands
yargs.command({
command: "add",
describe: "Add a new note",
handler: function () {
console.log("Adding a new note!");
},
});
yargs.command({
command: "remove",
describe: "Remove a note",
handler() {
console.log("Removing the note!");
},
});
console.log(yargs.argv);node app.js add #will print Adding a new note
node app.js —version will print current version of yargs setup
node app.js —help will print all commands setup
Yargs Builder
yargs.command({
command: "add",
describe: "Add a new note",
builder: {
title: {
describe: "Note title",
demandOption: true,
type: "string",
},
},
handler(argv) {
console.log("Title: " + argv.title);
},
});node app.js add will now show title as required and of type string.
Bcrypt
A library to help you hash passwords.
const bcrypt = require("bcrypt");
const hashFunction = async () => {
const password = "Red12345!";
const hashedPassword = await bcrypt.hash(password, 8);
const isMatch = await bcrypt.compare("Red12345!", hashedPassword);
console.log(isMatch);
};
hashFunction();Jsonwebtoken
npm i jsonwebtoken
Multer
Multi-part upload handler. Used for file uploads.
npm i multer
Sharp
Convert large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
npm i sharp
Debugging
Setup node debugging using inspect shell command:
node inspect app.js read --title "New Item 2"Edge
Goto URL edge://inspect
Chrome
Goto URL chrome://inspect
Breakpoint
Adding the following line of code in place where you want to pause execution of code:
debugger;After code execution is paused, local variables can be printed in the console using their respective names.
Debugger> restart (shell) can be used to restart session in chrome/edge after closing the console window.
Async Basics
Event loop can’t run async callbacks until call stack is empty.
console.log("START");
setTimeout(() => {
console.log("2 Second Timer");
}, 2000);
setTimeout(() => {
console.log("0 Second Timer");
}, 0);
console.log("STOP");Output:
START
STOP
0 Second Timer
2 Second TimerHTTP Requests
Can use following packages to make HTTP Request from NodeJS:
Error Handling
Axios
const params = {
access_key: "d4720a6be64ce712d4e1a31cf1d6caa4",
query: "12what",
};
const url = "http://api.weatherstack.com/current";
axios
.get(url, { params })
.then((response) => {
if (!response.data.success && response.data.error) throw 404;
else
console.log(
`${response.data.current.weather_descriptions[0]}. It is currently ${response.data.current.temperature} degress out. There is a ${response.data.current.precip}% chance of rain.`
);
})
.catch((error) => {
if (error === 404) {
console.log("Unable to find location.");
} else {
console.log("Unable to connect to location services.");
}
});Express Library
Fetch From Browser
Accesssing weather route to get weather
index.hbs
<body>
<div class="main-content">
{{>header}}
<p>Use this site to get your weather!</p>
<form>
<input placeholder="Location"/>
<button>Get Weather</button>
</form>
<p id="address"></p>
<p id="forecast"></p>
</div>
{{>footer}}
<script src="/js/app.js"></script>
</body>js/app.js:
const weatherForm = document.querySelector("form");
const search = document.querySelector("input");
const address = document.querySelector("#address");
const forecast = document.querySelector("#forecast");
weatherForm.addEventListener("submit", (e) => {
e.preventDefault();
address.textContent = "Loading...";
forecast.textContent = "";
fetch("/weather?location=" + search.value).then((response) => {
response.json().then((data) => {
if (data.error) {
address.textContent = data.error;
} else {
address.textContent = data.location;
forecast.textContent = data.forecast;
}
});
});
});nodejs: src/app.js:
app.get("/weather", (req, res) => {
if (!req.query.location) {
return res.send({
error: "You must provide a location",
});
}
params.query = req.query.location;
weatherInfo(params, (data, error) => {
if (error)
return res.send({
error: error,
});
return res.send(data);
});
});MongoDB
Mongoose
Authentication
User Schema Update
const userSchema = new mongoose.Schema({
//...
tokens: [
{
token: {
type: String,
required: true,
},
},
],
//...
});JWT (JSON Web Token)
const jwt = require("jsonwebtoken");
const tokenFunction = async () => {
const token = jwt.sign({ _id: "abc123" }, "thisismynewcourse", {
expiresIn: "7 days",
});
console.log(token);
const data = jwt.verify(token, "thisismynewcourse");
console.log(data);
};Generate JWT For A User
// JWT Token generate
userSchema.methods.generateAuthToken = async function () {
const user = this;
const token = jwt.sign({ _id: user._id.toString() }, "thisismynewcourse");
user.tokens = user.tokens.concat({ token });
await user.save();
return token;
};Login Request
Unauthorised request
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
res.send({ user, token });
} catch (error) {
res.status(400).send(error);
}
});Create User Request
Unauthorised request
router.post("/users", async (req, res) => {
const user = new User(req.body);
try {
await user.save();
const token = await user.generateAuthToken();
res.status(201).send({ user, token });
} catch (e) {
res.status(400).send(e);
}
});Middleware Auth
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const auth = async (req, res, next) => {
try {
const token = req.header("Authorization").replace("Bearer ", "");
const decoded = jwt.verify(token, "thisismynewcourse");
const user = await User.findOne({ _id: decoded._id, "tokens.token": token });
if (!user) throw new Error();
req.user = user; // storing user in req.user so that route handler can access it
req.token = token;
next();
} catch (error) {
res.status(401).send({ error: "Please authenticate." });
}
};
module.exports = auth;Login
const auth = require("../middleware/auth");
router.get("/users/profile", auth, async (req, res) => {
res.send(req.user);
});
Logout
router.post("/users/logout", auth, async (req, res) => {
try {
req.user.token = req.user.tokens.filter(
(token) => token.token !== req.token
);
await req.user.save();
res.send();
} catch (error) {
res.status(500).send(error);
}
});Logout All
Logout from all active sessions (devices).
router.post("/users/logoutAll", auth, async (req, res) => {
try {
req.user.tokens = [];
await req.user.save();
res.send();
} catch (error) {
res.status(500).send(error);
}
});Query Params
Data Filtering
router.get("/tasks", auth, async (req, res) => {
const match = {};
if (req.query.completed) {
match.completed = req.query.completed === "true";
}
try {
await req.user.populate({
path: "tasks",
match,
});
res.send(req.user.tasks);
} catch (error) {
res.status(500).send(error.message);
}
});If request url is in the form: {{host}}/tasks?completed=false then, match object will contain completed as false and only those tasks will be returned.
Pagination
// GET /tasks?limit=10&skip=0
router.get("/tasks", auth, async (req, res) => {
const options = {};
if (req.query.limit) {
options.limit = parseInt(req.query.limit);
}
if (req.query.skip) {
options.skip = parseInt(req.query.skip);
}
try {
await req.user.populate({
path: "tasks",
match,
options,
});
res.send(req.user.tasks);
} catch (error) {
res.status(500).send(error.message);
}
});Sorting
// GET /tasks?sortBy=createdAt:desc
router.get("/tasks", auth, async (req, res) => {
const options = {};
if (req.query.sortBy) {
const parts = req.query.sortBy.split(":");
options.sort = {};
options.sort[parts[0]] = parts[1] === "desc" ? -1 : 1;
}
try {
await req.user.populate({
path: "tasks",
match,
options,
});
res.send(req.user.tasks);
} catch (error) {
res.status(500).send(error.message);
}
});Websockets
ws package
import { WebSocketServer } from "ws";
import ws from "../src/config/ws.js";
const websocketport = process.env.WEBSOCKET_PORT || 443;
const wss = new WebSocketServer({
port: websocketport,
perMessageDeflate: false,
});
console.log(`WebSocket is running on port ${websocketport}`);
wss.on("connection", (ws) => {
console.log("Client connected");
clients.add(ws);
// Handle messages from the client
ws.on("message", (message) => {
console.log(`Received: ${message}`);
// Echo the message back to the client
ws.send(`Server received: ${message}`);
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
ws.on("close", () => {
console.log("Client disconnected");
clients.delete(ws);
});
});const clients = new Set();
const broadcast = (message) => {
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
};
export default { clients, broadcast };