NodeJS - herougo/SoftwareEngineerKnowledgeRepository GitHub Wiki
Source:
- codevolution 64-video series: https://www.youtube.com/playlist?list=PLC3y8-rFHvwh8shCMHFA5kWxD9PaPwxaY
Node.js Notes
- ECMAScript: industry standard for "JavaScript" for browsers to implement
- JavaScript Engine: "compiler" for JavaScript into machine code (e.g. Google's V8 engine)
- JavaScript Runtime: includes engine, api (e.g. document.ready, etc), event loop, etc
- Node.js IS a runtime environment
- it IS NOT a language or framework
- Node.js Runtime includes
- JS library
- C/C++ features
- dependencies: V8, libuv, zlib, crypto, etc
Hello World
node index.js # run index.js
Modules
Types of modules
-
built-in
-
local
-
third party modules
-
CommonJS is a standard that states how a module should be structured and shared (adopted by Node.js in its early days).
-
In node.js, each file is a module that is isolated by default
-
Before a module's code is executed, Node.js will wrap it with a function wrapper that provides module scope (i.e.
(function(...) { // module code }). Here are the parameters of said function.- exports - copy of module.exports
- require
- module
- __filename
- __dirname
-
module caching: sebsequent requires to the same module will not reload it (it is cached).
CommonJS Way of Doing Modules
const add = (a, b) => {
return a + b;
}
module.exports = add;
/* Can also do
module.exports = { add }
exports.add = add;
*/
const add = require('./add.js'); // require('./add'); also works and is common
ES Modules
- Introduced with ES2015
- export can be default or named
- default exports can be assigned any name while importing
- named exports must have the imported name be the same
- mjs is the file format for this and is only for node.js
const add = (a, b) => {
return a + b;
}
export default add;
/* Can also do
export default (a, b) => { ... }
export default { add }
*/
import add from './add.mjs'; // note the mjs
Alternatively
export const add = (a, b) => { ... };
import * as math from './add.mjs'; // note the mjs
// OR use import {add} from './add.mjs';
Unsorted
Can import json files as variables
const data = require("./data.json");
Built-In Modules
Path module
const path = require("node:path");
console.log(__filename); // /path/to/file.js
console.log(__dirname); // /path/to
console.log(path.basename(__filename)); // file.js
console.log(path.basename(__dirname)); // to
console.log(path.extname(__filename)); // .js
console.log(path.extname(__dirname)); //
console.log(path.isAbsolute(__filename)); // true
console.log(path.join("folder1", "folder2", "index.html"); // folder1/folder2/index.html
Events Module
- event: an action or an occurrence that has happened in our application that we can respond to
const EventEmitter = require("node:events");
const emitter = new EventEmitter();
emitter.on("order-pizza", (size) => {
console.log("Order received");
}
emitter.emit("order-pizza", "large"); // trigger event
const EventEmitter = require("node:events");
class PizzaShop extends EventEmitter {
constructor() {
super();
this.orderNumber = 0;
}
order(size, topping) {
this.orderNumber++;
this.emit("order", size, topping);
}
}
fs Module
const fs = require("node:fs");
const fileContents = fs.readFileSync("file.txt", "utf-8");
fs.readFile("file.txt", "utf-8", (error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
});
fs.writeFileSync("greet.txt", "Hello");
fs.writeFile("greet.txt", " world", { flag: "a" }, (err) => {
if (err) {
console.log(err);
} else {
console.log("File written");
}
});
Promises version of fs (less performant, but otherwise recommended)
const fs = require("node:fs/promises");
fs.readFile("file.txt", "utf-8")
.then((data) => console.log(data))
.catch((error) => console.log(error));
async function readFile() {
try {
const data = await fs.readFile("file.txt", "utf-8");
console.log(data);
} catch (err) {
console.log(err);
}
}
readFile();
Streams
Question: How is the data event emitted?
const readableStream = fs.createReadStream("file.txt", {encoding: "utf-8"});
const writeableStream = fs.createWriteStream("file2.txt");
readableStream.pipe(writeableStream);
// Alternatively
// readableStream.on("data", (chunk) => {
// console.log(chunk);
// writeableStream.write(chunk);
// });
http Module
Simple Node server (you should use a web framework instead for your own projects)
Content Type options
- text/plain
- application/json
- text/html
const http = require("node:http");
const server = http.createServer((req, res) => {
// req.method GET POST PUT DELETE
if (req.url === "/about") {
// ...
} else {
const superHero = {
firstName: "Bruce",
lastName: "Wayne",
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(superHero));
}
}
server.listen(3000, () => {
console.log("Server running on port 3000");
});
Express
const express = require("express");
const PORT = process.env.PORT || 3001;
const app = express();
app.use((req, res, next) => {
console.log("App-wide middleware");
next(); // runs next middleware/route
});
function apiSpecificMiddleware(req, res, next) {
console.log("API-specific middleware");
next();
// next(new Error("error")); // causes an error to occur in the middleware chain
}
// use async when dealing with a DB
app.get("/api", async (req, res) => {
console.log(`Saying hello`);
const dbResult = await prisma.post.findUnique(...);
res.send("Hello 1 from server!" );
});
app.get("/api2/:id", apiSpecificMiddleware, (req, res) => {
let id = req.params.id;
console.log(`Saying hello 2`);
res.send(`Hello 2 from server with ${id}!`);
});
// General error handler
app.use(function (err, req, res, next) {
if (process.env.NODE_ENV !== "test") console.error(err.stack);
const status = err.status || 500;
const message = err.message;
return res.status(status).json({
error: { message, status },
});
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
/* localhost:3001/api
App-wide middleware
Hello 1 from server!
*/
/* localhost:3001/api2/2
App-wide middleware
API-specific middleware
Hello 2 from server with 2!
*/
Routes
router = express.Router();
router.use(loggerMiddleware);
// method 1
router
.route("/:id")
.get(userGet)
.put(userPut)
.delete(userDelete);
// method 2
/*
router.get("/:id", userGet);
router.put("/:id", userGet);
router.delete("/:id", userGet);
*/
Express CORS
Source: https://expressjs.com/en/resources/middleware/cors.html#enable-cors-for-a-single-route
var cors = require('cors')
var corsOptions = {
origin: function (origin, callback) {
// db.loadOrigins is an example call to load
// a list of origins from a backing database
db.loadOrigins(function (error, origins) {
callback(error, origins)
})
}
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for an allowed domain.'})
})
Express morgan
Source: https://dev.to/devland/how-to-use-morgan-in-your-nodejs-project-21im
morgan is a logging library
app.use(morgan('tiny'));
// equivalent to
// app.use(morgan(':method :url :status :res[content-length] - :response-time ms'))
// prints e.g. "GET / 404 139 - 7.136 ms
Socket.io
Socket.IO - a library that enables low-latency, bidirectional and event-based communication between a client and a server.
By default, every user has a room with with their id
# server.js
const io = require("socket.io")(3000, {
cors: {
origin: ["http://localhost:8080"] // where the client lives
},
});
io.use((socket, next) => {
if (socket.handshake.auth.token) {
socket.username = getUsernameFromToken(socket.handshake.auth.token);
next();
} else {
next(new Error("Please send token"));
}
});
io.on("connection", socket => {
console.log(socket.id);
socket.on('send-message', (message, room) => {
console.log(message);
if (room === '') {
socket.broadcast.emit('receive-message', message);
// broadcast: to all clients but sender
// emit: send to all clients
} else {
socket.to(room).emit('receive-message', message);
// broadcast is assumed
}
});
socket.on("join-room", (room, cb) => {
socket.join(room);
cb(`Joined ${room}`);
})
});
# script.js
import { io } from 'socket.io-client';
const socket = io("http://localhost:3000");
socket.on('connect', () => {
displayMessage(`You connected with id: ${socket.id}`);
});
...
socket.emit("send-message", message, room);
...
socket.emit("join-room", room, message => {
displayMessage(message);
});
...