⬅️ NodeJS
- 🔗 from FEM Course Digging Into Node Introduction
Introduction
🤔 How do I/O compare to CPU tasks in terms of speed? I/O are 3-4x slower than CPU tasks which just happen in the processor
- the async event loop is a much better model than threads for the I/O tasks
- node gives you high throughput low latency I/O communication
Command-line scripts
🤔 What is POSIX? It’s a way that C-style programs interact with a linux system.
console.log('Hello world')
// is a wrapper for this but with a newline
process.stdout.write('Hello world')
// wrapper for the error stream
console.error()
# go and find node in whatever OS you're one
#! /usr/bin/env node
# making a file executable
chmod u+x file.js
process.argv
’s first two arguments: path to my node installation, full path to the file- 💡 You can run
process.argv.slice(2)
to chop them off!
// for parsing your argv (-- and - and flags and whatnot)
var args = require("minimist")(process.argv.slice(2))
// to check if a specific argument exists
if (args.help) doSomething()
File System
var contents = fs.readFileSync(filepath)
// if I console.log that I'll just get a buffer
// because console.log was trying to process the value
// to get the string
process.stdout.write(contents)
// or (this is slightly less efficient)
var contents = fs.readFileSync(filepath, 'utf8')
Streams
- simplex stream: you can either read or write to it
- duplex stream: you can do both
var stream1; // readable
var stream2; // writable
stream1.pipe(stream2);
// another readable stream
var stream3 = stream1.pipe(stream2);
let filePath = path.join(BASEPATH,args.file);
processFile(fs.createReadStream(filePath));
function processFile(inputStream) {
var stream = inputStream;
var outStream;
if (args.uncompress) {
let gunzip = zlib.createGunzip();
stream = stream.pipe(gunzip);
}
# var Transform = require("stream").Transform;
# this will transform one chunk at a time
var upperCaseTr = new Transform({
transform(chunk,encoding,callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
stream = stream.pipe(upperCaseTr);
if (args.compress) {
OUTPATH = `${OUTPATH}.gz`;
let gzip = zlib.createGzip();
stream = stream.pipe(gzip);
}
if (args.out) {
outStream = process.stdout;
}
else {
outStream = fs.createWriteStream(OUTPATH);
}
stream.pipe(outStream);
}
Gzipping
// a stream representation of gzip
var zlib = require("zlib");
# compressing
if (args.compress) {
OUTPATH = `${OUTPATH}.gz`;
let gzip = zlib.createGzip();
stream = stream.pipe(gzip);
}
# uncompressing
if (args.uncompress) {
let gunzip = zlib.createGunzip();
stream = stream.pipe(gunzip);
}
Waiting until the end of stream
// helper function
function streamComplete(stream){
return new Promise(function c(res){
stream.on("end",res);
});
}
await streamComplete(outStream)
🤔 What is the advantage of the streaming protocol? (aka why chunks?) Not only it’s more efficient it also uses less memory (65kb buffer size at a time, by default)
Database
🤔 What are advantages of using SqLite? Unlike MySQL, lite doesn’t require a db server, instead, it’s a serverless embedded db that runs as part of the application. It cannot connect to any other application over the network. It’s super lightweight and ideal for small sets of data.
How to join a table
async function getAllRecords() {
var result = await SQL3.all(
`
SELECT
Something.data AS "something",
Other.data AS "other"
FROM
Something
JOIN Other ON (Something.otherID = Other.id)
ORDER BY
Other.id DESC, Something.data
`
);
return result;
}
Web servers
Express routing and static file serving
#!/usr/bin/env node
"use strict";
var util = require("util");
var path = require("path");
var http = require("http");
var express = require("express");
var app = express();
var sqlite3 = require("sqlite3");
// ************************************
const DB_PATH = path.join(__dirname,"my.db");
const WEB_PATH = path.join(__dirname,"web");
const HTTP_PORT = 8039;
var delay = util.promisify(setTimeout);
// define some SQLite3 database helpers
// (comment out if sqlite3 not working for you)
var myDB = new sqlite3.Database(DB_PATH);
var SQL3 = {
run(...args) {
return new Promise(function c(resolve,reject){
myDB.run(...args,function onResult(err){
if (err) reject(err);
else resolve(this);
});
});
},
get: util.promisify(myDB.get.bind(myDB)),
all: util.promisify(myDB.all.bind(myDB)),
exec: util.promisify(myDB.exec.bind(myDB)),
};
var httpserv = http.createServer(app);
main();
// ************************************
function main() {
defineRoutes(app);
httpserv.listen(HTTP_PORT);
console.log(`Listening on http://localhost:${HTTP_PORT}...`);
}
function defineRoutes(app) {
app.get(/\/get-records\b/,async function getRecords(req,res){
await delay(1000);
let records = await getAllRecords() || [];
// res.json(records);
res.setHeader("Content-Type","application/json");
res.setHeader("Cache-Control","max-age: 0, no-cache");
res.writeHead(200);
res.end(JSON.stringify(records));
});
app.use(function rewriter(req,res,next){
if (/^\/(?:index\/?)?(?:[?#].*$)?$/.test(req.url)) {
req.url = "/index.html";
}
else if (/^\/js\/.+$/.test(req.url)) {
next();
return;
}
else if (/^\/(?:[\w\d]+)(?:[\/?#].*$)?$/.test(req.url)) {
let [,basename] = req.url.match(/^\/([\w\d]+)(?:[\/?#].*$)?$/);
req.url = `${basename}.html`;
}
next();
});
var fileServer = express.static(WEB_PATH,{
maxAge: 100,
setHeaders(res){
res.setHeader("Server","Node Workshop: ex6");
}
});
app.use(fileServer);
app.get(/\.html$/,function friendly404(req,res,next){
req.url = "/404.html";
fileServer(req,res,next);
});
}
// *************************
// NOTE: if sqlite3 is not working for you,
// comment this version out
// *************************
async function getAllRecords() {
var result = await SQL3.all(
`
SELECT
Something.data AS "something",
Other.data AS "other"
FROM
Something
JOIN Other ON (Something.otherID = Other.id)
ORDER BY
Other.id DESC, Something.data
`
);
return result;
}
Child process
- how to deal with node across multiple processes, specifically how to spin up child processes from our main process
- one way of testing whether a command was successfully run (aka non 0 exit code), is to chain another command after it. If the original command errored it will immediately exit and not run the next command, e.g.:
node index.js && ls -la
Debugging
- go to chrome inspect and when you run add the
--inspect
flag