Skip to main content

One Page Notes | Best Practices

Must Read​

Write Better Code with these 5 JavaScript Features

ES6 Modules and Node.js​

Tips

  • Use type='module' in package.json
  • .js file extension works fine. You dont need .mjs when type is module.
  • You don't need babel and is not recommended on the server side.
  • Follow this format for imports, and should not use require
    • Note the use of .js extension. For some reason it is mandatory, unlike react. Example: import { cmap, ctx } from '../ercli/commands.js'

Complete Express Server example​

app.js

import express from "express";
import bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import { logger } from "../util/logger.js";

// CAUTION: Remove in PROD if .env file is present
import dotenv from "dotenv";
dotenv.config();

import { ping, comm } from "./handlers.js";

const app = express();

import cors from "cors";

app.use(
cors({
origin: "http://localhost:3000",
})
);

app.use(bodyParser.json());
app.use(cookieParser());

app.get("/ping", ping);
app.post("/comm", comm);

const port = process.env.SERVER_PORT || 3030;
app.listen(port, () => {
logger.info(
`ErCommand SERVER listening on port ${port}! GET : http://localhost:${port}/ping`
);
});

handler.js

import { logger } from "../util/logger.js";
import { cmap, ctx } from "../ercli/commands.js";
import YAML from "yamljs";

// Meant for browser clients, token is set as cookie
const ping = (req, res) => {
res.send({ msg: "Hello from Auth Server" });
};

const comm = async (req, res) => {
logger.info(JSON.stringify(req.body));
const cmdJson =
req.body.format === "yaml" ? YAML.parse(req.body.cmd) : req.body.cmd;
logger.info(JSON.stringify(cmdJson));
const cmdPref = cmdJson.c;
let result = {};

if (cmdPref in cmap) {
// caution: single global singleton context added. Make it session bound
result = await cmap[cmdPref](cmdJson, ctx);
} else result = { msg: "command not found" };
res.send({ result });
};

export { ping, comm };

package.json

{
"name": "cli-server",
"version": "1.0.0",
"description": "server app",
"main": "app.js",
"type": "module",
"scripts": {
"dev": "node ./node_modules/nodemon/bin/nodemon.js server/app.js",
"start": "node server/app",
"test": "jest"
},
"author": "kre",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/preset-env": "^7.20.2",
"babel-jest": "^29.3.1",
"jest": "^29.3.1",
"nodemon": "^2.0.20"
},
"dependencies": {
"body-parser": "^1.18.3",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.16.4",
"jsonwebtoken": "^8.5.1",
"mongodb": "4.13",
"winston": "^3.8.2",
"yamljs": "^0.3.0"
}
}

Promises​

Error Handling​

An amazing Promise chaining pattern you should know,

fetch("/article/promise-chaining/user.json")
.then((response) => response.json())
.then((user) => fetch(`https://api.github.com/users/${user.name}`))
.then((response) => response.json())
.then(
(githubUser) =>
new Promise((resolve, reject) => {
let img = document.createElement("img");
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);

setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
})
)
.catch((error) => alert(error.message));

Another complete working example,

// ref: https://thomasstep.com/blog/a-guide-to-using-jwt-in-javascript
const { generateKeyPair } = require("jose");

async function genKeyPair() {
try {
const { publicKey, privateKey } = await generateKeyPair("RS256");
// https://nodejs.org/api/crypto.html#crypto_class_keyobject
const publicKeyString = publicKey.export({
type: "pkcs1-",
format: "pem",
});
const privateKeyString = privateKey.export({
type: "pkcs1",
format: "pem",
});
return [publicKeyString, privateKeyString];
} catch (e) {
const err = new Error(e.code || "Key gen error", { cause: e });
err.name = "KeyGenError";
throw err;
}
}

genKeyPair()
.then((result) => {
console.log(result);
})
.catch((err) => console.log(err.message || err));

JS Error Handling - Best Practices​

// Quick Custom Named error
} catch(e) {
const err = new Error(e.code || 'keygen error', {cause : e});err.name = 'KeyGenError';
throw err
}
// to catch the above
try {
//
} catch (e) {
switch (e.name) {
case "KeyGenError":
console.log(e.message || err);
// code block
break;
default:
console.log(e.message || err);
// code block
}
}

Error Object - Instance properties​

Error.prototype.message​

Error message. For user-created Error objects, this is the string provided as the constructor's first argument.

Error.prototype.name​

Error name. This is determined by the constructor function.

Error.prototype.cause​

Error cause indicating the reason why the current error is thrown — usually another caught error. For user-created Error objects, this is the value provided as the cause property of the constructor's second argument.

JS - Null Handling​

let k = "";
// Here t can be undefined or null as in k.t = null
console.log(k.t?.t);

NPM and NPX​

NPM vs NPX​

It is simple -> npx executes a script after doing npm install
Example npm "create-react-app": "node tasks/cra.js"

NPM usage​

  • npm test, npm start, npm restart, and npm stop are all aliases for npm run xxx.
  • For all other scripts you define, you need to use the npm run xxx syntax.
  • See the docs at https://docs.npmjs.com/cli/run-script for more information.

Check Outdated Packages​

Just do npm outdated, give you a list of versions. See bwlow example.

Package             Current  Wanted  Latest  Location                         Depended by
@next/font 13.1.1 13.1.3 13.1.3 node_modules/@next/font rwapp-complib-astra
axios 1.2.2 1.2.3 1.2.3 node_modules/axios rwapp-complib-astra
eslint 8.31.0 8.32.0 8.32.0 node_modules/eslint rwapp-complib-astra

Arrays​

Handy snippet - iteration, transform, copy to new​

Single line solution.

const kwd_arr_clean = [];
const kwd_arr = kwds.split(",");
kwd_arr.forEach((item) => kwd_arr_clean.push(item.trim()));

Promises​

Full file upload example with promises and Promise.all

const upload = (req, res) => {
const uploadBase = "./drop";
const pathName = "/p1";
const uploadPath = path.join(uploadBase, pathName);

if (req.files) {
logger.info("[File upload] received file");
const files = req.files;
var promises = [];

files.forEach((file) => {
const p = new Promise((resolve, reject) => {
createDirIfNotExists(uploadPath);
let fileName = path.join(uploadPath, file.originalname);
fs.writeFile(fileName, file.buffer, "binary", (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
promises.push(p);
});
const aggPromise_saveAllFiles = Promise.all(promises);

aggPromise_saveAllFiles
.then(() => res.send({ msg: "Success" }))
.catch((err) => {
res.send({ msg: err.message });
});
} else {
res.send({ msg: "Fail" });
}
};

Error handling inside Promises​

When you are inside a promise block, catch all await errors. Imagine what happens when an error is thrown inside a promise from an async function call.

Firestore example,

async function lookupCredentials(username, pwd) {
const dataRef = db.collection(USER_PROFILE_COLL);
const recSnapshot = await dataRef.where("username", "==", username).get();

const validatePromise = new Promise((resolve, reject) => {
if (recSnapshot.empty) {
reject("no matching records");
}

recSnapshot.forEach(async (doc) => {
const user_data = doc.data();
// console.log(doc.id, '=>', user_data);
// inject id
user_data.id = doc.id;
let val_result;
try {
val_result = await validateHash(pwd, user_data.pwdh);
} catch (val_err) {
reject(val_err);
}
// password validated against hash, user is valid
if (val_result) {
// remove pass hash for log safety
delete user_data.pwdh;
// resolve on first result as you expect only one record
resolve(user_data);
} else {
// val_result is false, password is wrong (against the stored hash)
reject("password missmatch error");
}
});
});

return validatePromise;
}

In this, the follow try..catch is critical, to avoid this nasty error,

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "[ERHASH11] hash mismatch error".] {
code: 'ERR_UNHANDLED_REJECTION'
}
try {
val_result = await validateHash(pwd, user_data.pwdh);
} catch (val_err) {
reject(val_err);
}

Promise Object​

Also check out other Promise object methods any, allSettled etc.
ref

JS Array, Map, JSON or Object iteration​

Check Out

Also check out Array, Map and Object specs.

Object.values() and Object.keys

Chain -> Map, Filter and Reduce calls​

Very useful pattern. Also check out - use of map vs forEach

let ages = data
.filter((animal) => {
return animal.type === "dog";
})
.map((animal) => {
return animal.age * 7;
})
.reduce((sum, animal) => {
return sum + animal.age;
});
// ages = 84

Use for....of for iteration instead of for...in​

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
Iterating over an Array

const iterable = [10, 20, 30];

for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30

Iterating over a Map

const iterable = new Map([
["a", 1],
["b", 2],
["c", 3],
]);

for (const entry of iterable) {
console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]

for (const [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3