├── .gitignore ├── Chapter02 ├── communicating-with-sockets │ ├── client.js │ └── server.js ├── fetching-metadata │ ├── .DS_Store │ ├── chmod-octal.js │ ├── chmod.js │ ├── file.txt │ └── metadata.js ├── file-watching │ ├── file.txt │ ├── package-lock.json │ ├── package.json │ └── watch.js ├── interfacing-with-io │ ├── greeting.js │ └── greeting2.js └── working-with-files │ ├── .DS_Store │ ├── hello.txt │ ├── readWriteAsync.js │ ├── readWriteAsync2.js │ ├── readWriteAsyncNamed.js │ ├── readWriteAsyncTimeout.js │ ├── readWritePromise.js │ └── readWriteSync.js ├── Chapter03 ├── learning-streams │ ├── async-generator.js │ ├── for-await-read-stream.js │ ├── infinite-read.js │ ├── paused-stream.js │ ├── read-stream.js │ └── write-stream.js ├── object-streams │ ├── object-stream.js │ ├── package-lock.json │ └── package.json ├── piping-streams │ ├── file.txt │ └── pipe-stream.js ├── stream-pipelines │ ├── file.txt │ ├── newFile.txt │ ├── pipeline.js │ └── promise-pipeline.js └── transform-stream │ ├── file.txt │ ├── newFile.txt │ ├── transform-stream-es6.js │ └── transform-stream.js ├── Chapter04 ├── file-upload │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── form.html │ └── server.js ├── http-server │ └── server.js ├── making-requests │ └── requests.js ├── post-server │ ├── json-server.js │ ├── public │ │ └── form.html │ └── server.js ├── server-smtp │ ├── package-lock.json │ ├── package.json │ ├── send-email.js │ └── server.js └── websocket-server │ ├── client.js │ ├── node-client.js │ ├── package-lock.json │ ├── package.json │ ├── public │ └── index.html │ └── server.js ├── Chapter05 ├── consuming-modules │ ├── package-lock.json │ ├── package.json │ └── require-express.js ├── ecmascript-modules │ ├── .gitignore │ ├── get-name │ │ └── index.mjs │ ├── package-lock.json │ ├── package.json │ └── server.mjs └── reverse-sentence │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── index.js │ ├── package-lock.json │ └── package.json ├── Chapter06 ├── express-app-ejs │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── routes │ │ └── index.js │ └── views │ │ └── index.ejs ├── express-app-generated │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ └── users.js │ └── views │ │ ├── error.ejs │ │ └── index.ejs ├── express-app │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ └── routes │ │ └── index.js ├── express-custom-middleware │ ├── app.js │ ├── middleware │ │ └── logger.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ └── routes │ │ └── index.js ├── express-post-app │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ └── routes │ │ └── index.js ├── fastify-app │ ├── package-lock.json │ ├── package.json │ ├── server.js │ └── test.js ├── fastify-generated-app │ ├── .gitignore │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── plugins │ │ ├── README.md │ │ └── support.js │ ├── services │ │ ├── README.md │ │ ├── example │ │ │ └── index.js │ │ └── root.js │ └── test │ │ ├── helper.js │ │ ├── plugins │ │ └── support.test.js │ │ └── services │ │ ├── example.test.js │ │ └── root.test.js ├── fastify-plugin-app │ ├── .vscode │ │ └── settings.json │ ├── package-lock.json │ ├── package.json │ ├── plugins │ │ └── hello-route.js │ ├── server.js │ └── test.js ├── hapi-app │ ├── .vscode │ │ └── settings.json │ ├── package-lock.json │ ├── package.json │ └── server.js ├── hapi-static-app │ ├── .vscode │ │ └── settings.json │ ├── files │ │ └── file.txt │ ├── package-lock.json │ ├── package.json │ └── server.js ├── hapi-static │ ├── .vscode │ │ └── settings.json │ ├── files │ │ └── file.txt │ ├── package-lock.json │ ├── package.json │ └── server.js ├── hapi-views-app │ ├── .vscode │ │ └── settings.json │ ├── package-lock.json │ ├── package.json │ ├── server.js │ └── views │ │ └── index.ejs ├── koa-app │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ └── routes │ │ └── index.js ├── koa-cascade │ ├── app.js │ ├── package-lock.json │ └── package.json ├── koa-custom-middleware │ ├── app.js │ ├── middleware │ │ └── logger.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ └── routes │ │ └── index.js └── koa-views-app │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ └── styles.css │ ├── routes │ └── index.js │ └── views │ └── index.ejs ├── Chapter07 ├── leveldb-app │ ├── package-lock.json │ ├── package.json │ └── tasks.js ├── leveldb-filter │ ├── filter.js │ ├── package-lock.json │ └── package.json ├── leveldb-memdown-app │ ├── package-lock.json │ ├── package.json │ └── tasks.js ├── mongodb-app │ ├── package-lock.json │ ├── package.json │ └── tasks.js ├── mongoose-app │ ├── customers.js │ └── package-lock.json ├── mysql-app │ ├── .env │ ├── insert.js │ ├── package-lock.json │ ├── package.json │ └── tasks.js ├── postgres-app │ ├── .env │ ├── package.json │ └── tasks.js ├── postgres-object-app │ ├── .env │ ├── package-lock.json │ └── tasks.js └── redis-app │ ├── package-lock.json │ ├── package.json │ ├── tasks-auth.js │ └── tasks.js ├── Chapter08 ├── enabling-travis │ ├── .gitignore │ ├── .travis.yml │ ├── package-lock.json │ ├── package.json │ └── test.js ├── stubbing-http-requests │ ├── github.js │ ├── package-lock.json │ ├── package.json │ └── test │ │ ├── octokitUserData.js │ │ └── test.js ├── testing-with-jest │ ├── coverage │ │ ├── clover.xml │ │ ├── coverage-final.json │ │ ├── lcov-report │ │ │ ├── base.css │ │ │ ├── block-navigation.js │ │ │ ├── favicon.png │ │ │ ├── index.html │ │ │ ├── prettify.css │ │ │ ├── prettify.js │ │ │ ├── sort-arrow-sprite.png │ │ │ ├── sorter.js │ │ │ └── uppercase.js.html │ │ └── lcov.info │ ├── package-lock.json │ ├── package.json │ ├── test │ │ └── test.js │ └── uppercase.js ├── testing-with-mocha │ ├── calculator.js │ ├── package-lock.json │ ├── package.json │ └── test │ │ └── test.js ├── testing-with-tape │ ├── calculator.js │ ├── package-lock.json │ ├── package.json │ └── test │ │ └── test.js └── using-puppeteer │ ├── package-lock.json │ ├── package.json │ └── test │ └── test.js ├── Chapter09 ├── audit-deps │ ├── package-lock.json │ └── package.json ├── express-auth │ ├── package-lock.json │ ├── package.json │ ├── routes │ │ ├── auth.js │ │ └── index.js │ ├── server.js │ └── views │ │ ├── index.ejs │ │ └── login.ejs ├── express-csrf │ ├── csrf-server.js │ ├── csurf-server.js │ ├── fixed-server.js │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-helmet │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-input │ ├── fixed-server.js │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-xss │ ├── collection-server.js │ ├── constraints-server.js │ ├── fixed-server.js │ ├── package-lock.json │ ├── package.json │ ├── protocol-safe-server.js │ └── server.js ├── hashing-with-bcrypt │ ├── hash.js │ ├── package-lock.json │ ├── package.json │ └── validate-password.js ├── http-app │ └── server.js └── json-pollution │ ├── fixed-server.js │ ├── package-lock.json │ └── server.js ├── Chapter10 ├── benchmarking-http │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── post-server.js │ ├── public │ │ ├── index.html │ │ └── stylesheets │ │ │ └── style.css │ └── routes │ │ ├── index.js │ │ └── users.js ├── benchmarking-views │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ └── users.js │ └── views │ │ ├── error.jade │ │ ├── index.jade │ │ └── layout.jade ├── flamegraph-app │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ └── users.js │ └── views │ │ ├── error.jade │ │ ├── index.jade │ │ └── layout.jade ├── optimize-async │ ├── calculate-average.js │ ├── package-lock.json │ ├── package.json │ ├── server-no-processing.js │ ├── server.js │ └── values.js ├── optimize-sync │ ├── benchmark.js │ ├── loop.js │ ├── package-lock.json │ ├── package.json │ └── slow.js ├── profiling-memory │ ├── leaky-server.js │ ├── max-listeners.js │ └── server.js └── worker-app │ ├── fibonacci-worker.js │ ├── fibonacci.js │ └── hello-worker.js ├── Chapter11 ├── bookstore-error-handling │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── inventory.js │ │ └── users.js │ └── views │ │ ├── error.ejs │ │ ├── index.ejs │ │ └── inventory.ejs ├── bookstore-web-app │ ├── .vscode │ │ └── settings.json │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ ├── inventory.js │ │ └── users.js │ └── views │ │ ├── error.ejs │ │ ├── index.ejs │ │ └── inventory.ejs ├── fastify-microservice │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Dockerfile-run │ ├── app.js │ ├── deployment │ │ ├── fastify-app-svc.yml │ │ └── fastify-app.yml │ ├── package.json │ ├── plugins │ │ ├── README.md │ │ └── support.js │ ├── routes │ │ ├── README.md │ │ ├── example │ │ │ └── index.js │ │ └── root.js │ └── test │ │ ├── helper.js │ │ ├── plugins │ │ └── support.test.js │ │ └── routes │ │ ├── example.test.js │ │ └── root.test.js └── loopback-bookstore │ ├── .dockerignore │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .mocharc.json │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── .vscode │ ├── settings.json │ └── tasks.json │ ├── .yo-rc.json │ ├── DEVELOPING.md │ ├── Dockerfile │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ └── index.html │ ├── src │ ├── __tests__ │ │ ├── README.md │ │ └── acceptance │ │ │ ├── home-page.acceptance.ts │ │ │ ├── ping.controller.acceptance.ts │ │ │ └── test-helper.ts │ ├── application.ts │ ├── controllers │ │ ├── README.md │ │ ├── books.controller.ts │ │ ├── index.ts │ │ └── ping.controller.ts │ ├── datasources │ │ ├── README.md │ │ ├── index.ts │ │ └── local.datasource.ts │ ├── index.ts │ ├── migrate.ts │ ├── models │ │ ├── README.md │ │ ├── book.model.ts │ │ └── index.ts │ ├── openapi-spec.ts │ ├── repositories │ │ ├── README.md │ │ ├── book.repository.ts │ │ └── index.ts │ └── sequence.ts │ └── tsconfig.json ├── Chapter12 ├── core-debug-logs │ ├── package-lock.json │ ├── package.json │ └── server.js ├── debugging-with-chrome │ ├── package-lock.json │ ├── package.json │ ├── random.js │ └── server.js ├── diagnostic-report │ └── server.js ├── express-debug-app │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-debug-custom │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-morgan-app │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ └── users.js │ └── views │ │ ├── error.ejs │ │ └── index.ejs ├── express-pino-app │ ├── package-lock.json │ ├── package.json │ └── server.js ├── express-winston-app │ ├── app.js │ ├── bin │ │ └── www │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ ├── routes │ │ ├── index.js │ │ └── users.js │ └── views │ │ ├── error.ejs │ │ └── index.ejs └── stack-trace-app │ ├── async-stack-trace.js │ ├── package-lock.json │ ├── package.json │ ├── routes.js │ └── server.js ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | -------------------------------------------------------------------------------- /Chapter02/communicating-with-sockets/client.js: -------------------------------------------------------------------------------- 1 | const net = require("net"); 2 | 3 | const HOSTNAME = "localhost"; 4 | const PORT = 3000; 5 | 6 | const socket = net.connect(PORT, HOSTNAME); 7 | 8 | socket.write("World"); 9 | 10 | socket.on("data", (data) => { 11 | console.log(data.toString()); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter02/communicating-with-sockets/server.js: -------------------------------------------------------------------------------- 1 | const net = require("net"); 2 | 3 | const HOSTNAME = "localhost"; 4 | const PORT = 3000; 5 | 6 | net 7 | .createServer((socket) => { 8 | console.log("Client connected."); 9 | 10 | socket.on("data", (name) => { 11 | socket.write(`Hello ${name}!`); 12 | }); 13 | }) 14 | .listen(PORT, HOSTNAME); 15 | -------------------------------------------------------------------------------- /Chapter02/fetching-metadata/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter02/fetching-metadata/.DS_Store -------------------------------------------------------------------------------- /Chapter02/fetching-metadata/chmod-octal.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const file = "./file.txt"; 3 | 4 | fs.chmodSync(file, 0o664); 5 | -------------------------------------------------------------------------------- /Chapter02/fetching-metadata/chmod.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const file = "./file.txt"; 3 | 4 | fs.chmodSync( 5 | file, 6 | fs.constants.S_IRUSR | 7 | fs.constants.S_IWUSR | 8 | fs.constants.S_IRGRP | 9 | fs.constants.S_IWGRP | 10 | fs.constants.S_IROTH 11 | ); 12 | -------------------------------------------------------------------------------- /Chapter02/fetching-metadata/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter02/fetching-metadata/file.txt -------------------------------------------------------------------------------- /Chapter02/fetching-metadata/metadata.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const file = process.argv[2]; 3 | 4 | function printMetadata(file) { 5 | try { 6 | const fileStats = fs.statSync(file); 7 | console.log(fileStats); 8 | } catch (err) { 9 | console.error("Error reading file path:", file); 10 | } 11 | } 12 | 13 | printMetadata(file); 14 | -------------------------------------------------------------------------------- /Chapter02/file-watching/file.txt: -------------------------------------------------------------------------------- 1 | dfadfHELLO! 2 | ls 3 | Hello again; 4 | gs 5 | gsgds 6 | gsdsdf 7 | sddsf 8 | sdfsd 9 | sdda 10 | dfsdf 11 | dsad 12 | sdasd 13 | -------------------------------------------------------------------------------- /Chapter02/file-watching/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-watching", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "moment": { 8 | "version": "2.24.0", 9 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 10 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter02/file-watching/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-watching", 3 | "version": "1.0.0", 4 | "main": "watch.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "moment": "^2.24.0" 13 | }, 14 | "devDependencies": {}, 15 | "description": "" 16 | } 17 | -------------------------------------------------------------------------------- /Chapter02/file-watching/watch.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const file = "./file.txt"; 3 | const moment = require("moment"); 4 | 5 | fs.watch(file, (eventType, filename) => { 6 | const time = moment().format("MMMM Do YYYY, h:mm:ss a"); 7 | return console.log(`${filename} updated ${time}`); 8 | }); 9 | -------------------------------------------------------------------------------- /Chapter02/interfacing-with-io/greeting.js: -------------------------------------------------------------------------------- 1 | process.stdin.on("data", (data) => { 2 | const name = data.toString().trim().toUpperCase(); 3 | process.stdout.write(`Hello ${name}!`); 4 | }); 5 | -------------------------------------------------------------------------------- /Chapter02/interfacing-with-io/greeting2.js: -------------------------------------------------------------------------------- 1 | process.stdin.on("data", (data) => { 2 | const name = data.toString().trim().toUpperCase(); 3 | if (name !== "") { 4 | process.stdout.write(`Hello ${name}!`); 5 | } else { 6 | process.stderr.write("Input was empty."); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter02/working-with-files/.DS_Store -------------------------------------------------------------------------------- /Chapter02/working-with-files/hello.txt: -------------------------------------------------------------------------------- 1 | HELLO WORLD! 2 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWriteAsync.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | fs.readFile(filepath, "utf8", (err, contents) => { 7 | if (err) { 8 | return console.log(err); 9 | } 10 | console.log("File Contents:", contents); 11 | const upperContents = contents.toUpperCase(); 12 | 13 | fs.writeFileSync(filepath, upperContents); 14 | console.log("File updated."); 15 | }); 16 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWriteAsync2.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | fs.readFile(filepath, "utf8", (err, contents) => { 7 | if (err) { 8 | return console.log(err); 9 | } 10 | console.log("File Contents:", contents); 11 | const upperContents = contents.toUpperCase(); 12 | 13 | fs.writeFile(filepath, upperContents, (err) => { 14 | if (err) throw err; 15 | console.log("File updated."); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWriteAsyncNamed.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | fs.readFile(filepath, "utf8", (err, contents) => { 7 | if (err) { 8 | return console.log(err); 9 | } 10 | console.log("File Contents:", contents); 11 | 12 | const upperContents = contents.toUpperCase(); 13 | updateFile(filepath, upperContents); 14 | }); 15 | 16 | function updateFile(filepath, contents) { 17 | fs.writeFile(filepath, contents, function (err) { 18 | if (err) throw err; 19 | console.log("File updated."); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWriteAsyncTimeout.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | fs.readFile(filepath, "utf8", (err, contents) => { 7 | if (err) { 8 | return console.log(err); 9 | } 10 | console.log("File Contents:", contents); 11 | const upperContents = contents.toUpperCase(); 12 | 13 | setTimeout(() => updateFile(filepath, upperContents), 10); 14 | }); 15 | 16 | function updateFile(filepath, contents) { 17 | fs.writeFile(filepath, contents, function (err) { 18 | if (err) throw err; 19 | console.log("File updated."); 20 | }); 21 | } 22 | 23 | setInterval(() => process.stdout.write("**** \n"), 1).unref(); 24 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWritePromise.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs").promises; 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | async function run() { 7 | try { 8 | const contents = await fs.readFile(filepath, "utf8"); 9 | console.log("File Contents:", contents); 10 | } catch (error) { 11 | console.error(error); 12 | } 13 | } 14 | 15 | run(); 16 | -------------------------------------------------------------------------------- /Chapter02/working-with-files/readWriteSync.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const filepath = path.join(process.cwd(), "hello.txt"); 5 | 6 | const contents = fs.readFileSync(filepath, "utf8"); 7 | console.log("File Contents:", contents); 8 | 9 | const upperContents = contents.toUpperCase(); 10 | 11 | fs.writeFileSync(filepath, upperContents); 12 | console.log("File updated."); 13 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/async-generator.js: -------------------------------------------------------------------------------- 1 | const { Readable } = require("stream"); 2 | 3 | async function* generate() { 4 | yield "Node.js"; 5 | yield "is"; 6 | yield "a"; 7 | yield "JavaScript"; 8 | yield "Runtime"; 9 | } 10 | 11 | const readable = Readable.from(generate()); 12 | 13 | readable.on("data", (chunk) => { 14 | console.log(chunk); 15 | }); 16 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/for-await-read-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const rs = fs.createReadStream("./file.txt"); 4 | 5 | async function run() { 6 | for await (const chunk of rs) { 7 | console.log("Read chunk:", chunk); 8 | } 9 | console.log("No more data."); 10 | } 11 | 12 | run(); 13 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/infinite-read.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const rs = fs.createReadStream("/dev/urandom"); 4 | 5 | let size = 0; 6 | rs.on("data", (data) => { 7 | size += data.length; 8 | console.log("File size:", size); 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/paused-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const rs = fs.createReadStream("./file.txt"); 4 | 5 | rs.on("readable", () => { 6 | // Read data 7 | let data = rs.read(); 8 | while (data !== null) { 9 | console.log("Read chunk:", data); 10 | data = rs.read(); 11 | } 12 | }); 13 | 14 | rs.on("end", () => { 15 | console.log("No more data."); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/read-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const rs = fs.createReadStream("./file.txt"); 4 | 5 | rs.on("data", (data) => { 6 | console.log("Read chunk:", data.toString()); 7 | }); 8 | 9 | rs.on("end", () => { 10 | console.log("No more data."); 11 | }); 12 | -------------------------------------------------------------------------------- /Chapter03/learning-streams/write-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const file = fs.createWriteStream("./file.txt"); 4 | 5 | for (let i = 0; i <= 1000000; i++) { 6 | file.write( 7 | "Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine.\n" 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /Chapter03/object-streams/object-stream.js: -------------------------------------------------------------------------------- 1 | const { Transform } = require("stream"); 2 | const { stringify } = require("ndjson"); 3 | 4 | const Name = Transform({ 5 | objectMode: true, 6 | transform: ({ forename, surname }, encoding, callback) => { 7 | callback(null, { name: forename + " " + surname }); 8 | }, 9 | }); 10 | 11 | Name.pipe(stringify()).pipe(process.stdout); 12 | 13 | Name.write({ forename: "John", surname: "Doe" }); 14 | Name.write({ forename: "Jane", surname: "Doe" }); 15 | -------------------------------------------------------------------------------- /Chapter03/object-streams/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-streams", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "object-stream.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "ndjson": "^2.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter03/piping-streams/file.txt: -------------------------------------------------------------------------------- 1 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 2 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 3 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 4 | 5 | -------------------------------------------------------------------------------- /Chapter03/piping-streams/pipe-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const rs = fs.createReadStream("file.txt"); 4 | 5 | rs.pipe(process.stdout); 6 | -------------------------------------------------------------------------------- /Chapter03/stream-pipelines/file.txt: -------------------------------------------------------------------------------- 1 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 2 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 3 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 4 | -------------------------------------------------------------------------------- /Chapter03/stream-pipelines/newFile.txt: -------------------------------------------------------------------------------- 1 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 2 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 3 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 4 | -------------------------------------------------------------------------------- /Chapter03/stream-pipelines/pipeline.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { pipeline, Transform } = require("stream"); 3 | 4 | const uppercase = new Transform({ 5 | transform(chunk, encoding, callback) { 6 | // Data processing 7 | callback(null, chunk.toString().toUpperCase()); 8 | }, 9 | }); 10 | 11 | pipeline( 12 | fs.createReadStream("./file.txt"), 13 | uppercase, 14 | fs.createWriteStream("./newFile.txt"), 15 | (err) => { 16 | if (err) { 17 | console.error("Pipeline failed.", err); 18 | } else { 19 | console.log("Pipeline succeeded."); 20 | } 21 | } 22 | ); 23 | -------------------------------------------------------------------------------- /Chapter03/stream-pipelines/promise-pipeline.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const stream = require("stream"); 3 | const util = require("util"); 4 | 5 | const pipeline = util.promisify(stream.pipeline); 6 | 7 | const uppercase = new stream.Transform({ 8 | transform(chunk, encoding, callback) { 9 | // Data processing 10 | callback(null, chunk.toString().toUpperCase()); 11 | }, 12 | }); 13 | 14 | async function run() { 15 | await pipeline( 16 | fs.createReadStream("./file.txt"), 17 | uppercase, 18 | fs.createWriteStream("./newFile.txt") 19 | ); 20 | console.log("Pipeline succeeded."); 21 | } 22 | 23 | run().catch((err) => { 24 | console.error("Pipeline failed.", err); 25 | }); 26 | -------------------------------------------------------------------------------- /Chapter03/transform-stream/file.txt: -------------------------------------------------------------------------------- 1 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 2 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 3 | Node.js is a JavaScript runtime built on Google Chrome's V8 JavaScript engine. 4 | -------------------------------------------------------------------------------- /Chapter03/transform-stream/newFile.txt: -------------------------------------------------------------------------------- 1 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 2 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 3 | NODE.JS IS A JAVASCRIPT RUNTIME BUILT ON GOOGLE CHROME'S V8 JAVASCRIPT ENGINE. 4 | -------------------------------------------------------------------------------- /Chapter03/transform-stream/transform-stream-es6.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { Transform } = require("stream"); 3 | 4 | const rs = fs.createReadStream("./file.txt"); 5 | const newFile = fs.createWriteStream("./newFile.txt"); 6 | 7 | class Uppercase extends Transform { 8 | constructor() { 9 | super(); 10 | } 11 | 12 | _transform(chunk, encoding, callback) { 13 | this.push(chunk.toString().toUpperCase()); 14 | callback(); 15 | } 16 | } 17 | 18 | rs.pipe(new Uppercase()).pipe(newFile); 19 | -------------------------------------------------------------------------------- /Chapter03/transform-stream/transform-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { Transform } = require("stream"); 3 | 4 | const rs = fs.createReadStream("./file.txt"); 5 | 6 | const newFile = fs.createWriteStream("./newFile.txt"); 7 | 8 | const uppercase = new Transform({ 9 | transform(chunk, encoding, callback) { 10 | // Data processing 11 | callback(null, chunk.toString().toUpperCase()); 12 | }, 13 | }); 14 | 15 | rs.pipe(uppercase).pipe(newFile); 16 | -------------------------------------------------------------------------------- /Chapter04/file-upload/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-upload", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "formidable": { 8 | "version": "1.2.2", 9 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", 10 | "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter04/file-upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-upload", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "formidable": "^1.2.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/file-upload/public/form.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | -------------------------------------------------------------------------------- /Chapter04/file-upload/server.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const http = require("http"); 3 | const path = require("path"); 4 | 5 | const form = fs.readFileSync(path.join(__dirname, "public", "form.html")); 6 | 7 | const formidable = require("formidable"); 8 | 9 | http 10 | .createServer((req, res) => { 11 | if (req.method === "GET") { 12 | get(res); 13 | return; 14 | } 15 | if (req.method === "POST") { 16 | post(req, res); 17 | return; 18 | } 19 | error(405, res); 20 | }) 21 | .listen(3000); 22 | 23 | function get(res) { 24 | res.writeHead(200, { 25 | "Content-Type": "text/html", 26 | }); 27 | res.end(form); 28 | } 29 | 30 | function error(code, res) { 31 | res.statusCode = code; 32 | res.end(http.STATUS_CODES[code]); 33 | } 34 | 35 | function post(req, res) { 36 | if (!/multipart\/form-data/.test(req.headers["content-type"])) { 37 | error(415, res); 38 | return; 39 | } 40 | 41 | const form = formidable({ 42 | multiples: true, 43 | uploadDir: "./uploads", 44 | }); 45 | 46 | form.parse(req, (err, fields, files) => { 47 | if (err) return err; 48 | res.writeHead(200, { 49 | "Content-Type": "application/json", 50 | }); 51 | res.end(JSON.stringify({fields,files,})); 52 | }); 53 | } -------------------------------------------------------------------------------- /Chapter04/http-server/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const HOSTNAME = process.env.HOSTNAME || "0.0.0.0"; 4 | const PORT = process.env.PORT || 3000; 5 | 6 | const server = http.createServer((req, res) => { 7 | if (req.method !== "GET") return error(res, 405); 8 | if (req.url === "/todo") return todo(res); 9 | if (req.url === "/") return index(res); 10 | error(res, 404); 11 | }); 12 | 13 | function error(res, code) { 14 | res.statusCode = code; 15 | res.end(`{"error": "${http.STATUS_CODES[code]}"}`); 16 | } 17 | 18 | function todo(res) { 19 | res.end('[{"task_id": 1, "description": "walk dog"}]}'); 20 | } 21 | 22 | function index(res) { 23 | res.end('{"name": "todo-server"}'); 24 | } 25 | 26 | server.listen(PORT, HOSTNAME, () => { 27 | console.log(`Server listening on port ${server.address().port}`); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter04/making-requests/requests.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const https = require("https"); 3 | 4 | // http.get("http://example.com", (res) => res.pipe(process.stdout)); 5 | 6 | const payload = `{ 7 | "name": "Beth", 8 | "job": "Software Engineer" 9 | }`; 10 | 11 | const opts = { 12 | method: "POST", 13 | hostname: "postman-echo.com", 14 | path: "/post", 15 | headers: { 16 | "Content-Type": "application/json", 17 | "Content-Length": Buffer.byteLength(payload), 18 | }, 19 | }; 20 | 21 | const req = https.request(opts, (res) => { 22 | process.stdout.write("Status Code: " + res.statusCode + "\n"); 23 | process.stdout.write("Body: "); 24 | res.pipe(process.stdout); 25 | }); 26 | 27 | req.on("error", (err) => console.error("Error: ", err)); 28 | 29 | req.end(payload); 30 | -------------------------------------------------------------------------------- /Chapter04/post-server/json-server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | 5 | const form = fs.readFileSync(path.join(__dirname, "public", "form.html")); 6 | 7 | http 8 | .createServer((req, res) => { 9 | if (req.method === "GET") { 10 | get(res); 11 | return; 12 | } 13 | 14 | if (req.method === "POST") { 15 | post(req, res); 16 | return; 17 | } 18 | error(405, res); 19 | }) 20 | .listen(3000); 21 | 22 | function get(res) { 23 | res.writeHead(200, { 24 | "Content-Type": "text/html", 25 | }); 26 | res.end(form); 27 | } 28 | 29 | function post(req, res) { 30 | if (req.headers["content-type"] !== "application/json") { 31 | error(415, res); 32 | return; 33 | } 34 | 35 | let input = ""; 36 | 37 | req.on("data", (chunk) => { 38 | input += chunk.toString(); 39 | }); 40 | 41 | req.on("end", () => { 42 | const parsed = JSON.parse(input); 43 | 44 | if (parsed.err) { 45 | error(400, "Bad Request", res); 46 | return; 47 | } 48 | 49 | console.log("Received data: ", parsed); 50 | res.end('{"data": ' + input + "}"); 51 | }); 52 | } 53 | 54 | function error(code, res) { 55 | res.statusCode = code; 56 | res.end(http.STATUS_CODES[code]); 57 | } 58 | -------------------------------------------------------------------------------- /Chapter04/post-server/public/form.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 31 | -------------------------------------------------------------------------------- /Chapter04/post-server/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | 5 | const form = fs.readFileSync(path.join(__dirname, "public", "form.html")); 6 | 7 | http 8 | .createServer((req, res) => { 9 | if (req.method === "GET") { 10 | get(res); 11 | return; 12 | } 13 | if (req.method === "POST") { 14 | post(req, res); 15 | return; 16 | } 17 | error(405, res); 18 | }) 19 | .listen(3000); 20 | 21 | function get(res) { 22 | res.writeHead(200, { 23 | "Content-Type": "text/html", 24 | }); 25 | res.end(form); 26 | } 27 | 28 | function post(req, res) { 29 | if (req.headers["content-type"] !== "application/x-www-form-urlencoded") { 30 | error(415, res); 31 | return; 32 | } 33 | 34 | let input = ""; 35 | 36 | req.on("data", (chunk) => { 37 | input += chunk.toString(); 38 | }); 39 | 40 | req.on("end", () => { 41 | console.log(input); 42 | res.end(http.STATUS_CODES[200]); 43 | }); 44 | } 45 | 46 | function error(code, res) { 47 | res.statusCode = code; 48 | res.end(http.STATUS_CODES[code]); 49 | } 50 | -------------------------------------------------------------------------------- /Chapter04/server-smtp/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-smtp", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "base32.js": { 8 | "version": "0.1.0", 9 | "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", 10 | "integrity": "sha1-tYLexpPC8R6JPPBk7mrFthMaIgI=" 11 | }, 12 | "ipv6-normalize": { 13 | "version": "1.0.1", 14 | "resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz", 15 | "integrity": "sha1-GzJYKQ02X6gyOeiZB93kWS52IKg=" 16 | }, 17 | "nodemailer": { 18 | "version": "6.4.6", 19 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.6.tgz", 20 | "integrity": "sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA==" 21 | }, 22 | "smtp-server": { 23 | "version": "3.6.0", 24 | "resolved": "https://registry.npmjs.org/smtp-server/-/smtp-server-3.6.0.tgz", 25 | "integrity": "sha512-DVEVWzL4s1GWzAs4+6rbhNZpAn61+V8l4b7R8zHLAW2jmlwKz9IKQmdgm5sNruCRnS01BYyitI98vJo7LDnXfg==", 26 | "requires": { 27 | "base32.js": "0.1.0", 28 | "ipv6-normalize": "1.0.1", 29 | "nodemailer": "6.4.5" 30 | }, 31 | "dependencies": { 32 | "nodemailer": { 33 | "version": "6.4.5", 34 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.5.tgz", 35 | "integrity": "sha512-NH7aNVQyZLAvGr2+EOto7znvz+qJ02Cb/xpou98ApUt5tEAUSVUxhvHvgV/8I5dhjKTYqUw0nasoKzLNBJKrDQ==" 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Chapter04/server-smtp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-smtp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "nodemailer": "^6.4.6", 15 | "smtp-server": "^3.6.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter04/server-smtp/send-email.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | 3 | const transporter = nodemailer.createTransport({ 4 | host: "localhost", 5 | port: 4321, 6 | }); 7 | 8 | transporter.sendMail( 9 | { 10 | from: "beth@example.com", 11 | to: "laddie@example.com", 12 | subject: "Hello", 13 | text: "Hello world!", 14 | }, 15 | (err, info) => { 16 | if (err) { 17 | console.log(err); 18 | } 19 | console.log("Message Sent:", info); 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /Chapter04/server-smtp/server.js: -------------------------------------------------------------------------------- 1 | const SMTPServer = require("smtp-server").SMTPServer; 2 | 3 | const PORT = 4321; 4 | 5 | const server = new SMTPServer({ 6 | disabledCommands: ["STARTTLS", "AUTH"], 7 | logger: true, 8 | }); 9 | 10 | server.on("error", (err) => { 11 | console.error(err); 12 | }); 13 | 14 | server.listen(PORT); 15 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/client.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const http = require("http"); 3 | 4 | const index = fs.readFileSync("public/index.html"); 5 | 6 | const server = http.createServer((req, res) => { 7 | res.setHeader("Content-Type", "text/html"); 8 | res.end(index); 9 | }); 10 | 11 | server.listen(8080); 12 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/node-client.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require("ws"); 2 | const ws = new WebSocket("ws://localhost:3000"); 3 | 4 | ws.on("open", () => { 5 | console.log("Connected"); 6 | }); 7 | 8 | ws.on("close", () => { 9 | console.log("Disconnected"); 10 | }); 11 | 12 | ws.on("message", (message) => { 13 | console.log("Received:", message); 14 | }); 15 | 16 | setInterval(() => { 17 | ws.send("Hello"); 18 | }, 3000); 19 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ws": { 8 | "version": "7.2.5", 9 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", 10 | "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "ws": "^7.2.5" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/public/index.html: -------------------------------------------------------------------------------- 1 |

Communicating with WebSockets

2 | 3 | 4 |
5 | 6 | 33 | -------------------------------------------------------------------------------- /Chapter04/websocket-server/server.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require("ws"); 2 | 3 | const WebSocketServer = new WebSocket.Server({ 4 | port: 3000, 5 | }); 6 | 7 | WebSocketServer.on("connection", (socket) => { 8 | socket.on("message", (msg) => { 9 | console.log("Received:", msg); 10 | if (msg === "Hello") socket.send("World!"); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter05/consuming-modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "consuming-modules", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "require-express.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1" 13 | }, 14 | "devDependencies": { 15 | "prettier": "2.0.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter05/consuming-modules/require-express.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | -------------------------------------------------------------------------------- /Chapter05/ecmascript-modules/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Chapter05/ecmascript-modules/get-name/index.mjs: -------------------------------------------------------------------------------- 1 | export const name = "Beth"; 2 | -------------------------------------------------------------------------------- /Chapter05/ecmascript-modules/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecmascript-modules", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter05/ecmascript-modules/server.mjs: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { name } from "./get-name/index.mjs"; 3 | 4 | const PORT = 3000; 5 | const app = express(); 6 | 7 | app.get("/", (req, res) => res.send(`Hello from ${name}!`)); 8 | 9 | app.listen(PORT, () => { 10 | console.log("Express server started on port", PORT); 11 | }); 12 | -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/.npmignore: -------------------------------------------------------------------------------- 1 | # Dependency directories 2 | node_modules/ -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/README.md: -------------------------------------------------------------------------------- 1 | # reverse-sentence 2 | 3 | Reverses the words of a sentence. 4 | 5 | ## Install 6 | 7 | ```sh 8 | npm install @bethany.griggs/reverse-sentence 9 | ``` 10 | 11 | ## API 12 | 13 | ```js 14 | require('reverse') => Function 15 | reverse(sentence) => String 16 | ``` 17 | 18 | ## Example 19 | 20 | ```js 21 | const reverseSentence = require("reverse-sentence"); 22 | 23 | const sentence = "Hello Beth!"; 24 | 25 | const reversed = reverseSentence(sentence); 26 | 27 | console.log(reversed); // Beth! Hello 28 | ``` 29 | 30 | ## License 31 | 32 | MIT 33 | -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/index.js: -------------------------------------------------------------------------------- 1 | module.exports = reverse; 2 | 3 | function reverse(sentence) { 4 | const wordsArray = sentence.split(" "); 5 | const reversedArray = wordsArray.reverse(); 6 | const reversedSentence = reversedArray.join(" "); 7 | return reversedSentence; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bethany.griggs/reverse-sentence", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "prettier": { 8 | "version": "2.0.5", 9 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", 10 | "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", 11 | "dev": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter05/reverse-sentence/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bethany.griggs/reverse-sentence", 3 | "version": "0.1.0", 4 | "description": "Reverses a sentence.", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "./node_modules/prettier/bin-prettier.js . --write", 8 | "prepublish": "npm run lint", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/BethGriggs/reverse-sentence.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/BethGriggs/reverse-sentence/issues" 19 | }, 20 | "homepage": "https://github.com/BethGriggs/reverse-sentence#readme", 21 | "devDependencies": { 22 | "prettier": "2.0.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter06/express-app-ejs/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const index = require("./routes/index"); 4 | 5 | const PORT = process.env.PORT || 3000; 6 | 7 | const app = express(); 8 | 9 | app.set("views", path.join(__dirname, "views")); 10 | app.set("view engine", "ejs"); 11 | 12 | app.use(express.static(path.join(__dirname, "public"))); 13 | app.use("/", index); 14 | 15 | app.listen(PORT, () => { 16 | console.log(`Server listening on port ${PORT}`); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter06/express-app-ejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "ejs": "^3.1.3", 14 | "express": "^4.17.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/express-app-ejs/public/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter06/express-app-ejs/public/styles.css -------------------------------------------------------------------------------- /Chapter06/express-app-ejs/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | router.get("/", (req, res) => { 5 | const title = "Express"; 6 | res.render("index", { 7 | title: "Express with EJS", 8 | }); 9 | }); 10 | 11 | module.exports = router; 12 | -------------------------------------------------------------------------------- /Chapter06/express-app-ejs/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 |

<%= title %>

10 |

Welcome to <%= title %>

11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/app.js: -------------------------------------------------------------------------------- 1 | var createError = require("http-errors"); 2 | var express = require("express"); 3 | var path = require("path"); 4 | var cookieParser = require("cookie-parser"); 5 | var logger = require("morgan"); 6 | 7 | var indexRouter = require("./routes/index"); 8 | var usersRouter = require("./routes/users"); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set("views", path.join(__dirname, "views")); 14 | app.set("view engine", "ejs"); 15 | 16 | app.use(logger("dev")); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: false })); 19 | app.use(cookieParser()); 20 | app.use(express.static(path.join(__dirname, "public"))); 21 | 22 | app.use("/", indexRouter); 23 | app.use("/users", usersRouter); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function (req, res, next) { 27 | next(createError(404)); 28 | }); 29 | 30 | // error handler 31 | app.use(function (err, req, res, next) { 32 | // set locals, only providing error in development 33 | res.locals.message = err.message; 34 | res.locals.error = req.app.get("env") === "development" ? err : {}; 35 | 36 | // render the error page 37 | res.status(err.status || 500); 38 | res.render("error"); 39 | }); 40 | 41 | module.exports = app; 42 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app-generated", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "http-errors": "~1.6.3", 14 | "morgan": "~1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00b7ff; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get("/", function (req, res, next) { 6 | res.render("index", { title: "Express" }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require("express"); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get("/", function (req, res, next) { 6 | res.send("respond with a resource"); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter06/express-app-generated/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter06/express-app/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const index = require("./routes/index"); 4 | 5 | const PORT = process.env.PORT || 3000; 6 | 7 | const app = express(); 8 | 9 | app.use(express.static(path.join(__dirname, "public"))); 10 | app.use("/", index); 11 | 12 | app.listen(PORT, () => { 13 | console.log(`Server listening on port ${PORT}`); 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter06/express-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/express-app/public/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter06/express-app/public/styles.css -------------------------------------------------------------------------------- /Chapter06/express-app/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | router.get("/", (req, res) => { 5 | const title = "Express"; 6 | res.send(` 7 | 8 | 9 | ${title} 10 | 11 | 12 |

${title}

13 |

Welcome to ${title}

14 | 15 | 16 | `); 17 | }); 18 | 19 | module.exports = router; 20 | -------------------------------------------------------------------------------- /Chapter06/express-custom-middleware/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const index = require("./routes/index"); 4 | const logger = require("./middleware/logger"); 5 | 6 | const PORT = process.env.PORT || 3000; 7 | 8 | const app = express(); 9 | 10 | app.use(logger()); 11 | app.use(express.static(path.join(__dirname, "public"))); 12 | app.use("/", index); 13 | 14 | app.listen(PORT, () => { 15 | console.log(`Server listening on port ${PORT}`); 16 | }); 17 | -------------------------------------------------------------------------------- /Chapter06/express-custom-middleware/middleware/logger.js: -------------------------------------------------------------------------------- 1 | module.exports = logger; 2 | 3 | function logger() { 4 | return (req, res, next) => { 5 | console.log("Request received:", req.method, req.url); 6 | next(); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter06/express-custom-middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/express-custom-middleware/public/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter06/express-custom-middleware/public/styles.css -------------------------------------------------------------------------------- /Chapter06/express-custom-middleware/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | router.get("/", (req, res) => { 5 | const title = "Express"; 6 | res.send(` 7 | 8 | 9 | ${title} 10 | 11 | 12 |

${title}

13 |

Welcome to ${title}

14 | 15 | 16 | `); 17 | }); 18 | 19 | module.exports = router; 20 | -------------------------------------------------------------------------------- /Chapter06/express-post-app/app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const index = require("./routes/index"); 4 | const bodyParser = require("body-parser"); 5 | 6 | const PORT = process.env.PORT || 3000; 7 | 8 | const app = express(); 9 | 10 | app.use( 11 | bodyParser.urlencoded({ 12 | extended: false, 13 | }) 14 | ); 15 | app.use(express.static(path.join(__dirname, "public"))); 16 | app.use("/", index); 17 | 18 | app.listen(PORT, () => { 19 | console.log(`Server listening on port ${PORT}`); 20 | }); 21 | -------------------------------------------------------------------------------- /Chapter06/express-post-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "express": "^4.17.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/express-post-app/public/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter06/express-post-app/public/styles.css -------------------------------------------------------------------------------- /Chapter06/express-post-app/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | router.get("/:name?", function (req, res) { 5 | const title = "Express"; 6 | const name = req.params.name; 7 | res.send(` 8 | 9 | 10 | ${title} 11 | 12 | 13 | 14 |

${title}

15 |

Welcome to ${title}${name ? `, ${name}.` : ""}

16 |
17 | Name: 18 |
19 | 20 | 21 | `); 22 | }); 23 | 24 | router.post("/data", function (req, res) { 25 | res.redirect(`/${req.body.name}`); 26 | }); 27 | 28 | module.exports = router; 29 | -------------------------------------------------------------------------------- /Chapter06/fastify-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "fastify": "^2.14.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/fastify-app/server.js: -------------------------------------------------------------------------------- 1 | const fastify = require("fastify")(); 2 | 3 | const PORT = process.env.PORT || 3000; 4 | 5 | // fastify.get("/", async (request, reply) => { 6 | // return { message: "Hello world!" }; 7 | // }); 8 | 9 | fastify.route({ 10 | method: "GET", 11 | url: "/", 12 | handler: async (request, reply) => { 13 | reply.send({ message: "Hello world!" }); 14 | }, 15 | }); 16 | 17 | const startServer = async () => { 18 | try { 19 | await fastify.listen(PORT); 20 | console.log(`server listening on ${fastify.server.address().port}`); 21 | } catch (err) { 22 | console.error(err); 23 | process.exit(1); 24 | } 25 | }; 26 | 27 | startServer(); 28 | -------------------------------------------------------------------------------- /Chapter06/fastify-app/test.js: -------------------------------------------------------------------------------- 1 | fastify.route({ 2 | method: "GET", 3 | url: "/", 4 | handler: function (request, reply) { 5 | reply.send({ message: "Hello world!" }); 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # 0x 40 | profile-* 41 | 42 | # mac files 43 | .DS_Store 44 | 45 | # vim swap files 46 | *.swp 47 | 48 | # webstorm 49 | .idea 50 | 51 | # vscode 52 | .vscode 53 | *code-workspace 54 | 55 | # clinic 56 | profile* 57 | *clinic* 58 | *flamegraph* 59 | 60 | # generated code 61 | examples/typescript-server.js 62 | test/types/index.js 63 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const AutoLoad = require('fastify-autoload') 5 | 6 | module.exports = function (fastify, opts, next) { 7 | // Place here your custom code! 8 | 9 | // Do not touch the following lines 10 | 11 | // This loads all plugins defined in plugins 12 | // those should be support plugins that are reused 13 | // through your application 14 | fastify.register(AutoLoad, { 15 | dir: path.join(__dirname, 'plugins'), 16 | options: Object.assign({}, opts) 17 | }) 18 | 19 | // This loads all plugins defined in services 20 | // define your routes in one of these 21 | fastify.register(AutoLoad, { 22 | dir: path.join(__dirname, 'services'), 23 | options: Object.assign({}, opts) 24 | }) 25 | 26 | // Make sure to call next when done 27 | next() 28 | } 29 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-generated", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "tap test/**/*.test.js", 11 | "start": "fastify start -l info app.js", 12 | "dev": "fastify start -w -l info -P app.js" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "fastify": "^2.0.0", 19 | "fastify-plugin": "^1.5.0", 20 | "fastify-autoload": "^1.0.0", 21 | "fastify-cli": "^1.5.0" 22 | }, 23 | "devDependencies": { 24 | "tap": "^12.5.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins Folder 2 | 3 | Plugins define behavior that is common to all the routes in your 4 | application. Authentication, caching, templates, and all the other cross 5 | cutting concerns should be handled by plugins placed in this folder. 6 | 7 | Files in this folder are typically defined through the 8 | [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module, 9 | making them non-encapsulated. They can define decorators and set hooks 10 | that will then be used in the rest of your application. 11 | 12 | Check out: 13 | 14 | * [The hitchhiker's guide to plugins](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md) 15 | * [Fastify decorators](https://www.fastify.io/docs/latest/Decorators/). 16 | * [Fastify lifecycle](https://www.fastify.io/docs/latest/Lifecycle/). 17 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/plugins/support.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | // the use of fastify-plugin is required to be able 6 | // to export the decorators to the outer scope 7 | 8 | module.exports = fp(function (fastify, opts, next) { 9 | fastify.decorate('someSupport', function () { 10 | return 'hugs' 11 | }) 12 | next() 13 | }) 14 | 15 | // If you prefer async/await, use the following 16 | // 17 | // module.exports = fp(async function (fastify, opts) { 18 | // fastify.decorate('someSupport', function () { 19 | // return 'hugs' 20 | // }) 21 | // }) 22 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/services/README.md: -------------------------------------------------------------------------------- 1 | # Services Folder 2 | 3 | Services define routes within your application. Fastify provides an 4 | easy path to a microservice architecture, in the future you might want 5 | to independently deploy some of those. 6 | 7 | In this folder you should define all the services that define the routes 8 | of your web application. 9 | Each service is a [Fastify 10 | plugin](https://www.fastify.io/docs/latest/Plugins/), it is 11 | encapsulated (it can have its own independent plugins) and it is 12 | typically stored in a file; be careful to group your routes logically, 13 | e.g. all `/users` routes in a `users.js` file. We have added 14 | a `root.js` file for you with a '/' root added. 15 | 16 | If a single file become too large, create a folder and add a `index.js` file there: 17 | this file must be a Fastify plugin, and it will be loaded automatically 18 | by the application. You can now add as many files as you want inside that folder. 19 | In this way you can create complex services within a single monolith, 20 | and eventually extract them. 21 | 22 | If you need to share functionality between services, place that 23 | functionality into the `plugins` folder, and share it via 24 | [decorators](https://www.fastify.io/docs/latest/Decorators/). 25 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/services/example/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (fastify, opts, next) { 4 | fastify.get('/example', function (request, reply) { 5 | reply.send('this is an example') 6 | }) 7 | 8 | next() 9 | } 10 | 11 | // If you prefer async/await, use the following 12 | // 13 | // module.exports = async function (fastify, opts) { 14 | // fastify.get('/example', async function (request, reply) { 15 | // return 'this is an example' 16 | // }) 17 | // } 18 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/services/root.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (fastify, opts, next) { 4 | fastify.get('/', function (request, reply) { 5 | reply.send({ root: true }) 6 | }) 7 | 8 | next() 9 | } 10 | 11 | // If you prefer async/await, use the following 12 | // 13 | // module.exports = async function (fastify, opts) { 14 | // fastify.get('/', async function (request, reply) { 15 | // return { root: true } 16 | // }) 17 | // } 18 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/test/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file contains code that we reuse 4 | // between our tests. 5 | 6 | const Fastify = require('fastify') 7 | const fp = require('fastify-plugin') 8 | const App = require('../app') 9 | 10 | // Fill in this config with all the configurations 11 | // needed for testing the application 12 | function config () { 13 | return {} 14 | } 15 | 16 | // automatically build and tear down our instance 17 | function build (t) { 18 | const app = Fastify() 19 | 20 | // fastify-plugin ensures that all decorators 21 | // are exposed for testing purposes, this is 22 | // different from the production setup 23 | app.register(fp(App), config()) 24 | 25 | // tear down our app after we are done 26 | t.tearDown(app.close.bind(app)) 27 | 28 | return app 29 | } 30 | 31 | module.exports = { 32 | config, 33 | build 34 | } 35 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/test/plugins/support.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const Fastify = require('fastify') 5 | const Support = require('../../plugins/support') 6 | 7 | test('support works standalone', (t) => { 8 | t.plan(2) 9 | const fastify = Fastify() 10 | fastify.register(Support) 11 | 12 | fastify.ready((err) => { 13 | t.error(err) 14 | t.equal(fastify.someSupport(), 'hugs') 15 | }) 16 | }) 17 | 18 | // If you prefer async/await, use the following 19 | // 20 | // test('support works standalone', async (t) => { 21 | // const fastify = Fastify() 22 | // fastify.register(Support) 23 | // 24 | // await fastify.ready() 25 | // t.equal(fastify.someSupport(), 'hugs') 26 | // }) 27 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/test/services/example.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const { build } = require('../helper') 5 | 6 | test('example is loaded', (t) => { 7 | t.plan(2) 8 | const app = build(t) 9 | 10 | app.inject({ 11 | url: '/example' 12 | }, (err, res) => { 13 | t.error(err) 14 | t.equal(res.payload, 'this is an example') 15 | }) 16 | }) 17 | 18 | // If you prefer async/await, use the following 19 | // 20 | // test('example is loaded', async (t) => { 21 | // const app = build(t) 22 | // 23 | // const res = await app.inject({ 24 | // url: '/example' 25 | // }) 26 | // t.equal(res.payload, 'this is an example') 27 | // }) 28 | -------------------------------------------------------------------------------- /Chapter06/fastify-generated-app/test/services/root.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const { build } = require('../helper') 5 | 6 | test('default root route', (t) => { 7 | t.plan(2) 8 | const app = build(t) 9 | 10 | app.inject({ 11 | url: '/' 12 | }, (err, res) => { 13 | t.error(err) 14 | t.deepEqual(JSON.parse(res.payload), { root: true }) 15 | }) 16 | }) 17 | 18 | // If you prefer async/await, use the following 19 | // 20 | // test('default root route', async (t) => { 21 | // const app = build(t) 22 | // 23 | // const res = await app.inject({ 24 | // url: '/' 25 | // }) 26 | // t.deepEqual(JSON.parse(res.payload), { root: true }) 27 | // }) 28 | -------------------------------------------------------------------------------- /Chapter06/fastify-plugin-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /Chapter06/fastify-plugin-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "fastify": "^2.14.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/fastify-plugin-app/plugins/hello-route.js: -------------------------------------------------------------------------------- 1 | async function routes(fastify) { 2 | fastify.get("/", async (request, reply) => { 3 | return { message: "Hello world!" }; 4 | }); 5 | } 6 | 7 | module.exports = routes; 8 | -------------------------------------------------------------------------------- /Chapter06/fastify-plugin-app/server.js: -------------------------------------------------------------------------------- 1 | const fastify = require("fastify")(); 2 | 3 | const PORT = process.env.PORT || 3000; 4 | 5 | fastify.register(require("./plugins/hello-route")); 6 | 7 | const startServer = async () => { 8 | try { 9 | await fastify.listen(PORT); 10 | console.log(`server listening on ${fastify.server.address().port}`); 11 | } catch (err) { 12 | console.error(err); 13 | process.exit(1); 14 | } 15 | }; 16 | 17 | startServer(); 18 | -------------------------------------------------------------------------------- /Chapter06/fastify-plugin-app/test.js: -------------------------------------------------------------------------------- 1 | fastify.route({ 2 | method: "GET", 3 | url: "/", 4 | handler: function (request, reply) { 5 | reply.send({ message: "Hello world!" }); 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /Chapter06/hapi-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } 4 | -------------------------------------------------------------------------------- /Chapter06/hapi-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@hapi/hapi": "^19.1.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/hapi-app/server.js: -------------------------------------------------------------------------------- 1 | const Hapi = require("@hapi/hapi"); 2 | 3 | const PORT = process.env.PORT || 3000; 4 | const HOSTNAME = process.env.HOSTNAME || "localhost"; 5 | 6 | const initialize = async () => { 7 | const server = Hapi.server({ 8 | port: PORT, 9 | host: HOSTNAME, 10 | }); 11 | 12 | server.route({ 13 | method: "GET", 14 | path: "/", 15 | handler: (request, h) => { 16 | return "Welcome to Hapi.js"; 17 | }, 18 | }); 19 | 20 | await server.start(); 21 | console.log("Server listening on", server.info.uri); 22 | }; 23 | 24 | initialize(); 25 | -------------------------------------------------------------------------------- /Chapter06/hapi-static-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } 4 | -------------------------------------------------------------------------------- /Chapter06/hapi-static-app/files/file.txt: -------------------------------------------------------------------------------- 1 | This is a static file. -------------------------------------------------------------------------------- /Chapter06/hapi-static-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@hapi/hapi": "^19.1.1", 14 | "@hapi/inert": "^6.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/hapi-static-app/server.js: -------------------------------------------------------------------------------- 1 | const Hapi = require("@hapi/hapi"); 2 | const path = require("path"); 3 | 4 | const PORT = process.env.PORT || 3000; 5 | const HOSTNAME = process.env.HOSTNAME || "localhost"; 6 | 7 | const initialize = async () => { 8 | const server = Hapi.server({ 9 | port: PORT, 10 | host: HOSTNAME, 11 | }); 12 | 13 | await server.register(require("@hapi/inert")); 14 | 15 | server.route({ 16 | method: "GET", 17 | path: "/", 18 | handler: { 19 | file: path.join(__dirname, "files/file.txt"), 20 | }, 21 | }); 22 | 23 | await server.start(); 24 | console.log("Server listening on", server.info.uri); 25 | }; 26 | 27 | initialize(); 28 | -------------------------------------------------------------------------------- /Chapter06/hapi-static/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } 4 | -------------------------------------------------------------------------------- /Chapter06/hapi-static/files/file.txt: -------------------------------------------------------------------------------- 1 | This is a static file. -------------------------------------------------------------------------------- /Chapter06/hapi-static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@hapi/hapi": "^19.1.1", 14 | "@hapi/inert": "^6.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/hapi-static/server.js: -------------------------------------------------------------------------------- 1 | const Hapi = require("@hapi/hapi"); 2 | const path = require("path"); 3 | 4 | const PORT = process.env.PORT || 3000; 5 | const HOSTNAME = process.env.HOSTNAME || "localhost"; 6 | 7 | const initialize = async () => { 8 | const server = Hapi.server({ 9 | port: PORT, 10 | host: HOSTNAME, 11 | }); 12 | 13 | await server.register(require("@hapi/inert")); 14 | 15 | server.route({ 16 | method: "GET", 17 | path: "/", 18 | handler: { 19 | file: path.join(__dirname, "files/file.txt"), 20 | }, 21 | }); 22 | 23 | await server.start(); 24 | console.log("Server listening on", server.info.uri); 25 | }; 26 | 27 | initialize(); 28 | -------------------------------------------------------------------------------- /Chapter06/hapi-views-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } 4 | -------------------------------------------------------------------------------- /Chapter06/hapi-views-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@hapi/hapi": "^19.1.1", 14 | "@hapi/vision": "^6.0.0", 15 | "ejs": "^3.1.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter06/hapi-views-app/server.js: -------------------------------------------------------------------------------- 1 | const Hapi = require("@hapi/hapi"); 2 | 3 | const PORT = process.env.PORT || 3000; 4 | const HOSTNAME = process.env.HOSTNAME || "localhost"; 5 | 6 | const initialize = async () => { 7 | const server = Hapi.server({ 8 | port: PORT, 9 | host: HOSTNAME, 10 | }); 11 | 12 | await server.register(require("@hapi/vision")); 13 | 14 | server.views({ 15 | engines: { 16 | ejs: require("ejs"), 17 | }, 18 | relativeTo: __dirname, 19 | path: "views", 20 | }); 21 | 22 | server.route({ 23 | method: "GET", 24 | path: "/", 25 | handler: function (request, h) { 26 | return h.view("index", { 27 | title: "Hapi.js", 28 | }); 29 | }, 30 | }); 31 | 32 | await server.start(); 33 | console.log("Server listening on", server.info.uri); 34 | }; 35 | 36 | initialize(); 37 | -------------------------------------------------------------------------------- /Chapter06/hapi-views-app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 |

<%= title %>

10 |

Welcome to <%= title %>

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter06/koa-app/app.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const Koa = require("koa"); 4 | const serve = require("koa-static"); 5 | const router = require("@koa/router")(); 6 | const index = require("./routes/index"); 7 | 8 | const PORT = process.env.PORT || 3000; 9 | 10 | const app = new Koa(); 11 | 12 | app.use(serve(path.join(__dirname, "public"))); 13 | 14 | router.use("/", index.routes()); 15 | app.use(router.routes()); 16 | 17 | app.listen(PORT, () => { 18 | console.log(`Server listening on port ${PORT}`); 19 | }); 20 | -------------------------------------------------------------------------------- /Chapter06/koa-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@koa/router": "^8.0.8", 14 | "koa": "^2.12.0", 15 | "koa-static": "^5.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter06/koa-app/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } -------------------------------------------------------------------------------- /Chapter06/koa-app/routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require("@koa/router")(); 2 | 3 | router.get("/", async function (ctx) { 4 | const title = "Koa.js"; 5 | ctx.body = ` 6 | 7 | 8 | ${title} 9 | 10 | 11 |

${title}

12 |

Welcome to ${title}

13 | 14 | 15 | `; 16 | }); 17 | 18 | module.exports = router; -------------------------------------------------------------------------------- /Chapter06/koa-cascade/app.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const app = new Koa(); 3 | 4 | app.use(async (ctx, next) => { 5 | console.log("First middleware start") 6 | await next(); 7 | console.log("First middleware return") 8 | }); 9 | 10 | app.use(async (ctx, next) => { 11 | console.log("Second middleware start") 12 | await next(); 13 | console.log("Second middleware return") 14 | 15 | }); 16 | 17 | app.use(async ctx => { 18 | console.log("Third middleware start"); 19 | console.log("Third middleware return"); 20 | }); 21 | 22 | app.listen(3000); -------------------------------------------------------------------------------- /Chapter06/koa-cascade/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-cascade", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "koa": "^2.12.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter06/koa-custom-middleware/app.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const Koa = require("koa"); 4 | const serve = require("koa-static"); 5 | const router = require("@koa/router")(); 6 | const index = require("./routes/index"); 7 | 8 | const logger = require("./middleware/logger"); 9 | 10 | const PORT = process.env.PORT || 3000; 11 | 12 | const app = new Koa(); 13 | 14 | app.use(logger()); 15 | app.use(serve(path.join(__dirname, "public"))); 16 | 17 | router.use("/", index.routes()); 18 | app.use(router.routes()); 19 | 20 | app.listen(PORT, () => { 21 | console.log(`Server listening on port ${PORT}`); 22 | }); 23 | -------------------------------------------------------------------------------- /Chapter06/koa-custom-middleware/middleware/logger.js: -------------------------------------------------------------------------------- 1 | module.exports = logger; 2 | 3 | function logger() { 4 | return async (ctx, next) => { 5 | console.log("Request received:", ctx.req.method, ctx.req.url); 6 | await next(); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter06/koa-custom-middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@koa/router": "^8.0.8", 14 | "koa": "^2.12.0", 15 | "koa-static": "^5.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter06/koa-custom-middleware/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /Chapter06/koa-custom-middleware/routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require("@koa/router")(); 2 | 3 | router.get("/", async function (ctx) { 4 | const title = "Koa.js"; 5 | ctx.body = ` 6 | 7 | 8 | ${title} 9 | 10 | 11 |

${title}

12 |

Welcome to ${title}

13 | 14 | 15 | `; 16 | }); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /Chapter06/koa-views-app/app.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const Koa = require("koa"); 4 | const serve = require("koa-static"); 5 | const router = require("@koa/router")(); 6 | const views = require('koa-views') 7 | const index = require("./routes/index"); 8 | 9 | const PORT = process.env.PORT || 3000; 10 | 11 | const app = new Koa(); 12 | 13 | 14 | app.use(views(path.join(__dirname, 'views'), { 15 | extension: 'ejs' 16 | })) 17 | 18 | app.use(serve(path.join(__dirname, "public"))); 19 | 20 | router.use("/", index.routes()); 21 | app.use(router.routes()); 22 | 23 | app.listen(PORT, () => { 24 | console.log(`Server listening on port ${PORT}`); 25 | }); -------------------------------------------------------------------------------- /Chapter06/koa-views-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@koa/router": "^8.0.8", 14 | "ejs": "^3.1.3", 15 | "koa": "^2.12.0", 16 | "koa-ejs": "^4.3.0", 17 | "koa-static": "^5.0.0", 18 | "koa-views": "^6.2.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter06/koa-views-app/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /Chapter06/koa-views-app/routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require("@koa/router")(); 2 | 3 | router.get("/", async function (ctx, next) { 4 | ctx.state = { 5 | title: "Koa.js", 6 | }; 7 | await ctx.render("index"); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /Chapter06/koa-views-app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 | 9 |

<%= title %>

10 |

Welcome to <%= title %>

11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter07/leveldb-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leveldb-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "tasks.js", 6 | "dependencies": { 7 | "level": "^6.0.1", 8 | "leveldown": "^5.6.0", 9 | "levelup": "^4.4.0" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /Chapter07/leveldb-app/tasks.js: -------------------------------------------------------------------------------- 1 | const levelup = require("levelup"); 2 | const leveldown = require("leveldown"); 3 | 4 | const db = levelup(leveldown("./data")); 5 | 6 | const task = process.argv[2]; 7 | 8 | if (!task) { 9 | listTasks(); 10 | } else { 11 | addTask(); 12 | } 13 | 14 | function addTask() { 15 | const key = `Task: ${Math.random().toString(32).replace(".", "")}`; 16 | db.put(key, task, (err) => { 17 | if (err) throw err; 18 | listTasks(); 19 | }); 20 | } 21 | 22 | function listTasks() { 23 | db.createReadStream().on("data", (data) => { 24 | console.log(data.key.toString(), "=", data.value.toString()); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /Chapter07/leveldb-filter/filter.js: -------------------------------------------------------------------------------- 1 | const levelup = require("levelup"); 2 | const leveldown = require("leveldown"); 3 | const db = levelup(leveldown("./data")); 4 | 5 | db.put("Task:1", ""); 6 | db.put("Task:2", ""); 7 | db.put("Task:3", ""); 8 | db.put("Task:4", ""); 9 | 10 | db.createReadStream({ 11 | gte: "Task:1", 12 | lte: "Task:3", 13 | }).on("data", function (data) { 14 | console.log(data.key.toString()); 15 | }); 16 | 17 | db.batch() 18 | .put("forename", "Beth") 19 | .put("surname", "Griggs") 20 | .del("forename") 21 | .write(() => console.log("Batch operations complete.")); 22 | -------------------------------------------------------------------------------- /Chapter07/leveldb-filter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leveldb-filter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "filter.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "leveldown": "^5.6.0", 14 | "levelup": "^4.4.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter07/leveldb-memdown-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leveldb-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "tasks.js", 6 | "dependencies": { 7 | "level": "^6.0.1", 8 | "levelup": "^4.4.0", 9 | "memdown": "^5.1.0" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /Chapter07/leveldb-memdown-app/tasks.js: -------------------------------------------------------------------------------- 1 | const levelup = require("levelup"); 2 | const memdown = require("memdown"); 3 | 4 | const db = levelup(memdown("./data")); 5 | 6 | const task = process.argv[2]; 7 | 8 | if (!task) { 9 | listTasks(); 10 | } else { 11 | addTask(); 12 | } 13 | 14 | function addTask() { 15 | const key = `Task: ${Math.random().toString(32).replace(".", "")}`; 16 | db.put(key, task, (err) => { 17 | if (err) throw err; 18 | listTasks(); 19 | }); 20 | } 21 | 22 | function listTasks() { 23 | db.createReadStream().on("data", (data) => { 24 | console.log(data.key.toString(), "=", data.value.toString()); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /Chapter07/mongodb-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongodb-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "tasks.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "mongodb": "^3.5.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter07/mongodb-app/tasks.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require("mongodb"); 2 | const task = process.argv[2]; 3 | 4 | const URI = "mongodb://localhost:27017/"; 5 | 6 | MongoClient.connect( 7 | URI, 8 | { 9 | useUnifiedTopology: true, 10 | }, 11 | connected 12 | ); 13 | 14 | function connected(err, client) { 15 | if (err) throw err; 16 | const tasks = client.db("tasklist").collection("tasks"); 17 | 18 | if (task) { 19 | addTask(client, tasks); 20 | } else { 21 | listTasks(client, tasks); 22 | } 23 | } 24 | 25 | function addTask(client, tasks) { 26 | tasks.insertOne( 27 | { 28 | task: task, 29 | }, 30 | (err) => { 31 | if (err) throw err; 32 | console.log("New Task: ", task); 33 | listTasks(client, tasks); 34 | } 35 | ); 36 | } 37 | 38 | function listTasks(client, tasks) { 39 | tasks.find().each((err, doc) => { 40 | if (err) throw err; 41 | if (!doc) { 42 | client.close(); 43 | return; 44 | } 45 | console.log(doc); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /Chapter07/mongoose-app/customers.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const URI = "mongodb://localhost:27017/customers"; 3 | 4 | mongoose.connect(URI, { 5 | useNewUrlParser: true, 6 | useUnifiedTopology: true, 7 | }); 8 | 9 | const Customer = mongoose.model("Customer", { 10 | forename: String, 11 | surname: String, 12 | }); 13 | 14 | const customer1 = new Customer({ 15 | forename: "Beth", 16 | surname: "Griggs", 17 | }); 18 | 19 | customer1.save().then((doc) => { 20 | console.log("Added new customer:", doc.forename, doc.surname); 21 | listCustomers(); 22 | }); 23 | 24 | function listCustomers() { 25 | console.log("Customers:"); 26 | Customer.find().then((doc) => { 27 | doc.forEach((customer) => { 28 | console.log(`- ${customer.surname}, ${customer.forename}`); 29 | mongoose.connection.close(); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /Chapter07/mysql-app/.env: -------------------------------------------------------------------------------- 1 | DB_MYSQL_USER=root 2 | DB_MYSQL_PASSWORD=PASSWORD -------------------------------------------------------------------------------- /Chapter07/mysql-app/insert.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const mysql = require("mysql"); 3 | 4 | const db = mysql.createConnection({ 5 | user: process.env.DB_MYSQL_USER, 6 | password: process.env.DB_MYSQL_PASSWORD, 7 | }); 8 | 9 | db.query("CREATE DATABASE tasks"); 10 | db.query("USE tasks"); 11 | 12 | db.query( 13 | "CREATE TABLE tasks.tasks (" + 14 | "id INT NOT NULL AUTO_INCREMENT, " + 15 | "task TEXT NOT NULL, PRIMARY KEY ( id )" + 16 | ")" 17 | ); 18 | 19 | const ignore = new Set(["ER_DB_CREATE_EXISTS", "ER_TABLE_EXISTS_ERROR"]); 20 | 21 | db.on("error", (err) => { 22 | if (ignore.has(err.code)) return; 23 | throw err; 24 | }); 25 | 26 | if (process.argv[2]) { 27 | db.query( 28 | ` 29 | INSERT INTO tasks.tasks (task) 30 | VALUES (?); 31 | `, 32 | [process.argv[2]] 33 | ); 34 | } 35 | 36 | db.query( 37 | ` 38 | SELECT * FROM tasks.tasks; 39 | `, 40 | (err, results, fields) => { 41 | console.log(results); 42 | } 43 | ); 44 | 45 | db.end(); 46 | -------------------------------------------------------------------------------- /Chapter07/mysql-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mysql-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "mysql": "^2.18.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter07/mysql-app/tasks.js: -------------------------------------------------------------------------------- 1 | const mysql = require("mysql"); 2 | require("dotenv").config(); 3 | 4 | const db = mysql.createConnection({ 5 | user: process.env.DB_MYSQL_USER, 6 | password: process.env.DB_MYSQL_PASSWORD, 7 | }); 8 | 9 | db.query("CREATE DATABASE tasks"); 10 | db.query("USE tasks"); 11 | 12 | db.query(` 13 | CREATE TABLE tasks.tasks ( 14 | id INT NOT NULL AUTO_INCREMENT, 15 | task TEXT NOT NULL, PRIMARY KEY ( id )) 16 | `); 17 | 18 | const ignore = new Set(["ER_DB_CREATE_EXISTS", "ER_TABLE_EXISTS_ERROR"]); 19 | 20 | db.on("error", (err) => { 21 | if (ignore.has(err.code)) return; 22 | throw err; 23 | }); 24 | 25 | db.query(` 26 | INSERT INTO tasks.tasks (task) 27 | VALUES ("Walk the dog."); 28 | `); 29 | 30 | db.query( 31 | ` 32 | SELECT * FROM tasks.tasks; 33 | `, 34 | (err, results, fields) => { 35 | console.log(results); 36 | db.end(); 37 | } 38 | ); 39 | -------------------------------------------------------------------------------- /Chapter07/postgres-app/.env: -------------------------------------------------------------------------------- 1 | PGUSER=postgres 2 | PGPASSWORD=PASSWORD 3 | PGPORT=5432 -------------------------------------------------------------------------------- /Chapter07/postgres-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgres-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "tasks.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^8.2.0", 14 | "pg": "^8.2.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter07/postgres-app/tasks.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const pg = require("pg"); 3 | const db = new pg.Client(); 4 | const task = process.argv[2]; 5 | 6 | const CREATE_TABLE_SQL = `CREATE TABLE IF NOT EXISTS tasks 7 | (id SERIAL, task TEXT NOT NULL, PRIMARY KEY ( id ))`; 8 | const INSERT_TASK_SQL = `INSERT INTO tasks (task) VALUES ($1);`; 9 | const GET_TASKS_SQL = `SELECT * FROM tasks;`; 10 | 11 | db.connect((err) => { 12 | if (err) throw err; 13 | db.query(CREATE_TABLE_SQL, (err) => { 14 | if (err) throw err; 15 | if (task) { 16 | db.query(INSERT_TASK_SQL, [task], (err) => { 17 | if (err) throw err; 18 | listTasks(); 19 | }); 20 | } else { 21 | listTasks(); 22 | } 23 | }); 24 | }); 25 | 26 | function listTasks() { 27 | db.query(GET_TASKS_SQL, (err, results) => { 28 | if (err) throw err; 29 | console.log(results.rows); 30 | db.end(); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /Chapter07/postgres-object-app/.env: -------------------------------------------------------------------------------- 1 | PGUSER=postgres 2 | PGPASSWORD=PASSWORD 3 | PGPORT=5432 -------------------------------------------------------------------------------- /Chapter07/postgres-object-app/tasks.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const pg = require("pg"); 3 | const db = new pg.Client(); 4 | const task = process.argv[2]; 5 | 6 | const CREATE_TABLE_SQL = `CREATE TABLE IF NOT EXISTS task_docs 7 | (id SERIAL, doc jsonb);`; 8 | const INSERT_TASK_SQL = `INSERT INTO task_docs (doc) VALUES ($1);`; 9 | const GET_TASKS_SQL = `SELECT * FROM task_docs;`; 10 | 11 | db.connect((err) => { 12 | if (err) throw err; 13 | db.query(CREATE_TABLE_SQL, (err) => { 14 | if (err) throw err; 15 | if (task) { 16 | db.query(INSERT_TASK_SQL, [task], (err) => { 17 | if (err) throw err; 18 | listTasks(); 19 | }); 20 | } else { 21 | listTasks(); 22 | } 23 | }); 24 | }); 25 | 26 | function listTasks() { 27 | db.query(GET_TASKS_SQL, (err, results) => { 28 | if (err) throw err; 29 | console.log(results.rows); 30 | db.end(); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /Chapter07/redis-app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis-app", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "denque": { 8 | "version": "1.4.1", 9 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", 10 | "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" 11 | }, 12 | "redis": { 13 | "version": "3.0.2", 14 | "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", 15 | "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", 16 | "requires": { 17 | "denque": "^1.4.1", 18 | "redis-commands": "^1.5.0", 19 | "redis-errors": "^1.2.0", 20 | "redis-parser": "^3.0.0" 21 | } 22 | }, 23 | "redis-commands": { 24 | "version": "1.5.0", 25 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", 26 | "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" 27 | }, 28 | "redis-errors": { 29 | "version": "1.2.0", 30 | "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", 31 | "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" 32 | }, 33 | "redis-parser": { 34 | "version": "3.0.0", 35 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", 36 | "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", 37 | "requires": { 38 | "redis-errors": "^1.0.0" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter07/redis-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "redis": "^3.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter07/redis-app/tasks-auth.js: -------------------------------------------------------------------------------- 1 | const redis = require("redis"); 2 | const client = redis.createClient({ 3 | port: 6380, 4 | password: "PASSWORD", 5 | }); 6 | const task = process.argv[2]; 7 | 8 | client.on("error", (err) => { 9 | console.log("Error:", err); 10 | }); 11 | 12 | if (!task) { 13 | listTasks(); 14 | } else { 15 | addTask(task); 16 | } 17 | 18 | function addTask(task) { 19 | const key = `Task: ${Math.random().toString(32).replace(".", "")}`; 20 | client.hmset(key, { 21 | task, 22 | }); 23 | listTasks(); 24 | } 25 | 26 | function listTasks() { 27 | client.keys("Task:*", (err, keys) => { 28 | if (err) throw err; 29 | keys.forEach((key) => { 30 | client.hgetall(key, (err, task) => { 31 | if (err) throw err; 32 | console.log(task); 33 | }); 34 | }); 35 | client.quit(); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /Chapter07/redis-app/tasks.js: -------------------------------------------------------------------------------- 1 | const redis = require("redis"); 2 | const client = redis.createClient(); 3 | const task = process.argv[2]; 4 | 5 | client.on("error", (err) => { 6 | console.log("Error:", err); 7 | }); 8 | 9 | if (!task) { 10 | listTasks(); 11 | } else { 12 | addTask(task); 13 | } 14 | 15 | function addTask(task) { 16 | const key = `Task: ${Math.random().toString(32).replace(".", "")}`; 17 | client.hmset(key, { 18 | task, 19 | }); 20 | listTasks(); 21 | } 22 | 23 | function listTasks() { 24 | client.keys("Task:*", (err, keys) => { 25 | if (err) throw err; 26 | keys.forEach((key) => { 27 | client.hgetall(key, (err, task) => { 28 | if (err) throw err; 29 | console.log(task); 30 | }); 31 | }); 32 | client.quit(); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /Chapter08/enabling-travis/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 14 -------------------------------------------------------------------------------- /Chapter08/enabling-travis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enabling-travis", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bethgriggs/enabling-travis.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/bethgriggs/enabling-travis/issues" 18 | }, 19 | "homepage": "https://github.com/bethgriggs/enabling-travis#readme", 20 | "devDependencies": { 21 | "tape": "^5.0.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter08/enabling-travis/test.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | test("test integer addition", function (t) { 3 | t.plan(1); 4 | t.equal(1 + 1, 2); 5 | }); 6 | 7 | test("test string addition", function (t) { 8 | t.plan(1); 9 | // Expected to fail 10 | t.equal("1" + "1", 2); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /Chapter08/stubbing-http-requests/github.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | 3 | module.exports.getGitHubUser = (username) => { 4 | return fetch("https://api.github.com/users/" + username) 5 | .then((res) => res.json()) 6 | .then((json) => { 7 | return json; 8 | }); 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /Chapter08/stubbing-http-requests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stubbing-http-requests", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "node-fetch": "^2.6.0", 14 | "tape": "^5.0.1" 15 | }, 16 | "devDependencies": { 17 | "sinon": "^9.0.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter08/stubbing-http-requests/test/octokitUserData.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | login: "octokit", 3 | id: 3430433, 4 | node_id: "MDEyOk9yZ2FuaXphdGlvbjM0MzA0MzM=", 5 | avatar_url: "https://avatars0.githubusercontent.com/u/3430433?v=4", 6 | gravatar_id: "", 7 | url: "https://api.github.com/users/octokit", 8 | html_url: "https://github.com/octokit", 9 | followers_url: "https://api.github.com/users/octokit/followers", 10 | following_url: "https://api.github.com/users/octokit/following{/other_user}", 11 | gists_url: "https://api.github.com/users/octokit/gists{/gist_id}", 12 | starred_url: "https://api.github.com/users/octokit/starred{/owner}{/repo}", 13 | subscriptions_url: "https://api.github.com/users/octokit/subscriptions", 14 | organizations_url: "https://api.github.com/users/octokit/orgs", 15 | repos_url: "https://api.github.com/users/octokit/repos", 16 | events_url: "https://api.github.com/users/octokit/events{/privacy}", 17 | received_events_url: "https://api.github.com/users/octokit/received_events", 18 | type: "Organization", 19 | site_admin: false, 20 | name: "Octokit", 21 | company: null, 22 | blog: "http://octokit.github.io", 23 | location: null, 24 | email: null, 25 | hireable: null, 26 | bio: "Official clients for the GitHub API", 27 | twitter_username: "octokit", 28 | public_repos: 44, 29 | public_gists: 0, 30 | followers: 0, 31 | following: 0, 32 | created_at: "2013-01-30T18:13:42Z", 33 | updated_at: "2020-06-09T20:53:23Z", 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /Chapter08/stubbing-http-requests/test/test.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const sinon = require("sinon"); 3 | 4 | const github = require("../github.js"); 5 | const octokitUserData = require("./octokitUserData.js"); 6 | 7 | test("Get GitHub user by username", async function (t) { 8 | t.plan(3); 9 | 10 | sinon.stub(github, "getGitHubUser").returns(octokitUserData); 11 | 12 | const githubUser = await github.getGitHubUser("octokit"); 13 | 14 | t.equal(githubUser.id, 3430433); 15 | t.equal(githubUser.login, "octokit"); 16 | t.equal(githubUser.name, "Octokit"); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/clover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/coverage-final.json: -------------------------------------------------------------------------------- 1 | {"/Users/bethgriggs/Node-Cookbook/Chapter08/testing-with-jest/uppercase.js": {"path":"/Users/bethgriggs/Node-Cookbook/Chapter08/testing-with-jest/uppercase.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":3,"column":2}},"1":{"start":{"line":2,"column":2},"end":{"line":2,"column":30}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":17},"end":{"line":1,"column":18}},"loc":{"start":{"line":1,"column":29},"end":{"line":3,"column":1}},"line":1}},"branchMap":{},"s":{"0":1,"1":1},"f":{"0":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"4681eeca6d557aa8d2e52343f597c58417a4bbf7"} 2 | } 3 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/lcov-report/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter08/testing-with-jest/coverage/lcov-report/favicon.png -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Node.js-14-Cookbook/b697d5f5cbd36282ee4dc21024d3bd842dc363fc/Chapter08/testing-with-jest/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/coverage/lcov.info: -------------------------------------------------------------------------------- 1 | TN: 2 | SF:uppercase.js 3 | FN:1,(anonymous_0) 4 | FNF:1 5 | FNH:1 6 | FNDA:1,(anonymous_0) 7 | DA:1,1 8 | DA:2,1 9 | LF:2 10 | LH:2 11 | BRF:0 12 | BRH:0 13 | end_of_record 14 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testing-with-jest", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "jest": "^26.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/test/test.js: -------------------------------------------------------------------------------- 1 | const uppercase = require("./../uppercase"); 2 | 3 | describe("uppercase", () => { 4 | test("uppercase hello returns HELLO", () => { 5 | expect(uppercase("hello")).toBe("HELLO"); 6 | }); 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /Chapter08/testing-with-jest/uppercase.js: -------------------------------------------------------------------------------- 1 | module.exports = (string) => { 2 | return string.toUpperCase(); 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /Chapter08/testing-with-mocha/calculator.js: -------------------------------------------------------------------------------- 1 | module.exports.add = (number1, number2) => { 2 | return number1 + number2; 3 | }; 4 | 5 | module.exports.subtract = (number1, number2) => { 6 | return number1 - number2; 7 | }; 8 | 9 | module.exports.multiply = (number1, number2) => { 10 | return number1 * number2; 11 | }; 12 | 13 | module.exports.divide = (number1, number2) => { 14 | return number1 / number2; 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /Chapter08/testing-with-mocha/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chapter08", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "chai": "^4.2.0", 14 | "mocha": "^8.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter08/testing-with-mocha/test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require("chai").assert; 2 | const calculator = require("../calculator.js"); 3 | 4 | describe("Calculator Test", function () { 5 | describe("add() Test", function () { 6 | it("add(1,2) should return 3", function () { 7 | assert.equal(calculator.add(1, 2), 3); 8 | }); 9 | 10 | it("add('1','2') should return 3", function () { 11 | assert.equal(calculator.add("1", "2"), 3); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter08/testing-with-tape/calculator.js: -------------------------------------------------------------------------------- 1 | module.exports.add = (number1, number2) => { 2 | return number1 + number2; 3 | }; 4 | 5 | module.exports.subtract = (number1, number2) => { 6 | return number1 - number2; 7 | }; 8 | 9 | module.exports.multiply = (number1, number2) => { 10 | return number1 * number2; 11 | }; 12 | 13 | module.exports.divide = (number1, number2) => { 14 | return number1 / number2; 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /Chapter08/testing-with-tape/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testing-with-tape", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "tape": "^5.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/testing-with-tape/test/test.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const calculator = require("./../calculator.js"); 3 | 4 | test("test add integers 1 and 2", (t) => { 5 | t.plan(1); 6 | t.equal(calculator.add(1, 2), 3); 7 | }); 8 | 9 | test("test add strings 1 and 2", (t) => { 10 | t.plan(1); 11 | t.equal(calculator.add("1", "2"), 3); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter08/using-puppeteer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "using-puppeteer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "puppeteer": "^4.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/using-puppeteer/test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const puppeteer = require("puppeteer"); 3 | 4 | async function runTest() { 5 | const browser = await puppeteer.launch(); 6 | const page = await browser.newPage(); 7 | await page.goto("https://example.com"); 8 | const title = await page.$eval("h1", (el) => el.innerText); 9 | 10 | console.log("Title value:", title); 11 | assert.equal(title, "Example Domain"); 12 | 13 | browser.close(); 14 | } 15 | 16 | runTest(); 17 | -------------------------------------------------------------------------------- /Chapter09/audit-deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audit-deps", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.15.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter09/express-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-auth", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "ejs": "^3.1.5", 15 | "express": "^4.17.1", 16 | "express-session": "^1.17.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter09/express-auth/routes/auth.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const router = Router(); 3 | 4 | router.get("/login", function (req, res, next) { 5 | res.render("login", { fail: false }); 6 | next(); 7 | }); 8 | 9 | router.post("/login", function (req, res, next) { 10 | if (req.session.user) { 11 | res.redirect("/"); 12 | next(); 13 | return; 14 | } 15 | if (req.body.username === "beth" && req.body.password === "badpassword") { 16 | req.session.user = { name: req.body.username }; 17 | res.redirect("/"); 18 | next(); 19 | return; 20 | } 21 | 22 | res.render("login", { fail: true }); 23 | next(); 24 | }); 25 | 26 | router.get("/logout", function (req, res, next) { 27 | req.session.user = null; 28 | res.redirect("/"); 29 | }); 30 | 31 | module.exports = router; 32 | -------------------------------------------------------------------------------- /Chapter09/express-auth/routes/index.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const router = Router(); 3 | 4 | router.get("/", function (req, res) { 5 | const user = req.session.user; 6 | res.render("index", { user }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter09/express-auth/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require("body-parser"); 3 | const session = require("express-session"); 4 | const { join } = require("path"); 5 | 6 | const index = require("./routes/index"); 7 | const auth = require("./routes/auth"); 8 | 9 | const app = express(); 10 | 11 | app.set("views", join(__dirname, "views")); 12 | app.set("view engine", "ejs"); 13 | 14 | app.use( 15 | session({ 16 | name: "SESSIONID", 17 | secret: "Node Cookbook", 18 | resave: false, 19 | saveUninitialized: false, 20 | }) 21 | ); 22 | 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | 25 | app.use("/", index); 26 | app.use("/auth", auth); 27 | 28 | app.listen(3000, () => { 29 | console.log("Server listening on port 3000"); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /Chapter09/express-auth/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | Express 4 | 5 | 6 |

Express

7 |

Welcome to Express

8 | <% if (user) { %> 9 |

Hello <%= user.name %>!

10 |

Logout

11 | <% } else { %> 12 |

Login

13 | <% } %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter09/express-auth/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | Express - Login 4 | 5 | 6 |

Login

7 | <% if (fail) { %> 8 |

Login Failed.

9 | <% } %> 10 |
11 | Username: 12 | Password: 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter09/express-csrf/csrf-server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const attackerEmail = "attacker@example.com"; 4 | 5 | const server = http.createServer((req, res) => { 6 | res.writeHead(200, { "Content-Type": "text/html" }); 7 | res.end(` 8 | 9 |
10 | 11 | 12 |
`); 13 | }); 14 | 15 | server.listen(3001, () => { 16 | console.log("Server listening on port 3001"); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter09/express-csrf/fixed-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require("body-parser"); 3 | const session = require("express-session"); 4 | const app = express(); 5 | 6 | const mockUser = { 7 | username: "beth", 8 | password: "badpassword", 9 | email: "beth@example.com", 10 | }; 11 | 12 | app.use( 13 | session({ 14 | secret: "Node Cookbook", 15 | name: "SESSIONID", 16 | resave: false, 17 | saveUninitialized: false, 18 | cookie: { sameSite: true }, 19 | }) 20 | ); 21 | 22 | app.use(bodyParser.urlencoded({ extended: false })); 23 | 24 | app.get("/", (req, res) => { 25 | if (req.session.user) return res.redirect("/account"); 26 | res.send(` 27 |

Social Media Account - Login

28 |
29 | 30 | 31 | 32 |
33 | `); 34 | }); 35 | 36 | app.post("/", (req, res) => { 37 | if ( 38 | req.body.username === mockUser.username && 39 | req.body.password === mockUser.password 40 | ) { 41 | req.session.user = req.body.username; 42 | } 43 | if (req.session.user) res.redirect("/account"); 44 | else res.redirect("/"); 45 | }); 46 | 47 | app.get("/account", (req, res) => { 48 | if (!req.session.user) return res.redirect("/"); 49 | res.send(` 50 |

Social Media Account - Settings

51 |

Email: ${mockUser.email}

52 |
53 | 54 | 55 |
56 | `); 57 | }); 58 | 59 | app.post("/update", (req, res) => { 60 | if (!req.session.user) return res.sendStatus(403); 61 | mockUser.email = req.body.email; 62 | res.redirect("/"); 63 | }); 64 | 65 | app.listen(3000, () => { 66 | console.log("Server listening on port 3000"); 67 | }); 68 | -------------------------------------------------------------------------------- /Chapter09/express-csrf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-csrf", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "csurf": "^1.11.0", 15 | "express": "^4.17.1", 16 | "express-session": "^1.17.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter09/express-csrf/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bodyParser = require("body-parser"); 3 | const session = require("express-session"); 4 | const app = express(); 5 | 6 | const mockUser = { 7 | username: "beth", 8 | password: "badpassword", 9 | email: "beth@example.com", 10 | }; 11 | 12 | app.use( 13 | session({ 14 | secret: "Node Cookbook", 15 | name: "SESSIONID", 16 | resave: false, 17 | saveUninitialized: false, 18 | }) 19 | ); 20 | 21 | app.use(bodyParser.urlencoded({ extended: false })); 22 | 23 | app.get("/", (req, res) => { 24 | if (req.session.user) return res.redirect("/account"); 25 | res.send(` 26 |

Social Media Account - Login

27 |
28 | 29 | 30 | 31 |
32 | `); 33 | }); 34 | 35 | app.post("/", (req, res) => { 36 | if ( 37 | req.body.username === mockUser.username && 38 | req.body.password === mockUser.password 39 | ) { 40 | req.session.user = req.body.username; 41 | } 42 | if (req.session.user) res.redirect("/account"); 43 | else res.redirect("/"); 44 | }); 45 | 46 | app.get("/account", (req, res) => { 47 | if (!req.session.user) return res.redirect("/"); 48 | res.send(` 49 |

Social Media Account - Settings

50 |

Email: ${mockUser.email}

51 |
52 | 53 | 54 |
55 | `); 56 | }); 57 | 58 | app.post("/update", (req, res) => { 59 | if (!req.session.user) return res.sendStatus(403); 60 | mockUser.email = req.body.email; 61 | res.redirect("/"); 62 | }); 63 | 64 | app.listen(3000, () => { 65 | console.log("Server listening on port 3000"); 66 | }); 67 | -------------------------------------------------------------------------------- /Chapter09/express-helmet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-helmet", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1", 14 | "helmet": "^4.1.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter09/express-helmet/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const helmet = require("helmet"); 4 | 5 | app.use(helmet()); 6 | 7 | app.get("/", (req, res) => res.send("Hello World!")); 8 | 9 | app.listen(3000, () => { 10 | console.log("Server listening on port 3000"); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /Chapter09/express-input/fixed-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => { 5 | asyncWork(() => { 6 | let msg = req.query.msg; 7 | if (Array.isArray(msg)) msg = msg.pop(); 8 | const upper = (msg || "").toUpperCase(); 9 | res.send(upper); 10 | }); 11 | }); 12 | 13 | asyncWork = (callback) => { 14 | setTimeout(callback, 0); 15 | }; 16 | 17 | app.listen(3000, () => { 18 | console.log("Server listening on port 3000"); 19 | }); 20 | -------------------------------------------------------------------------------- /Chapter09/express-input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-input", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter09/express-input/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => { 5 | asyncWork(() => { 6 | const upper = (req.query.msg || "").toUpperCase(); 7 | res.send(upper); 8 | }); 9 | }); 10 | 11 | asyncWork = (callback) => { 12 | setTimeout(callback, 0); 13 | }; 14 | 15 | app.listen(3000, () => { 16 | console.log("Server listening on port 3000"); 17 | }); 18 | -------------------------------------------------------------------------------- /Chapter09/express-xss/collection-server.js: -------------------------------------------------------------------------------- 1 | require("http") 2 | .createServer((req, res) => { 3 | console.log( 4 | req.connection.remoteAddress, 5 | Buffer.from(req.url.split("/attack/")[1], "base64").toString().trim() 6 | ); 7 | }) 8 | .listen(3001, () => { 9 | console.log("Collection Server listening on port 3001"); 10 | }); 11 | -------------------------------------------------------------------------------- /Chapter09/express-xss/constraints-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => { 5 | const { previous, lang, token } = req.query; 6 | 7 | if (!validateParameters({ previous, token, lang }, req.query)) { 8 | res.sendStatus(422); 9 | return; 10 | } 11 | 12 | getServiceStatus((status) => { 13 | res.send(` 14 |

Service Status

15 |
16 | ${status} 17 |
18 |
19 | Back 20 |
21 | `); 22 | }); 23 | }); 24 | 25 | getServiceStatus = (callback) => { 26 | const status = "All systems are running."; 27 | callback(status); 28 | }; 29 | 30 | validateParameters = ({ previous, token, lang }, query) => { 31 | return ( 32 | Object.keys(query).length <= 3 && 33 | typeof lang === "string" && 34 | lang.length === 2 && 35 | typeof token === "string" && 36 | token.length === 16 && 37 | typeof previous === "string" && 38 | previous.length <= 16 39 | ); 40 | }; 41 | 42 | app.listen(3000, () => { 43 | console.log("Server listening on port 3000"); 44 | }); 45 | -------------------------------------------------------------------------------- /Chapter09/express-xss/fixed-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const he = require("he"); 4 | 5 | app.get("/", (req, res) => { 6 | const { previous, lang, token } = req.query; 7 | getServiceStatus((status) => { 8 | const href = he.encode(`${previous}${token}/${lang}`); 9 | res.send(` 10 |

Service Status

11 |
12 | ${status} 13 |
14 |
15 | Back 16 |
17 | `); 18 | }); 19 | }); 20 | 21 | getServiceStatus = (callback) => { 22 | const status = "All systems are running."; 23 | callback(status); 24 | }; 25 | 26 | app.listen(3000, () => { 27 | console.log("Server listening on port 3000"); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter09/express-xss/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-xss", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "escape-html": "^1.0.3", 14 | "express": "^4.17.1", 15 | "he": "^1.2.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter09/express-xss/protocol-safe-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const escapeHTML = require("escape-html"); 4 | 5 | app.get("/", (req, res) => { 6 | const { previous, lang, token } = req.query; 7 | getServiceStatus((status) => { 8 | const href = escapeHTML(`/${previous}${token}/${lang}`); 9 | res.send(` 10 |

Service Status

11 |
12 | ${status} 13 |
14 |
15 | Back 16 |
17 | `); 18 | }); 19 | }); 20 | 21 | getServiceStatus = (callback) => { 22 | const status = "All systems are running."; 23 | callback(status); 24 | }; 25 | 26 | app.listen(3000, () => { 27 | console.log("Server listening on port 3000"); 28 | }); 29 | -------------------------------------------------------------------------------- /Chapter09/express-xss/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => { 5 | const { previous, lang, token } = req.query; 6 | getServiceStatus((status) => { 7 | res.send(` 8 |

Service Status

9 |
10 | ${status} 11 |
12 |
13 | Back 14 |
15 | `); 16 | }); 17 | }); 18 | 19 | getServiceStatus = (callback) => { 20 | const status = "All systems are running."; 21 | callback(status); 22 | }; 23 | 24 | app.listen(3000, () => { 25 | console.log("Server listening on port 3000"); 26 | }); 27 | -------------------------------------------------------------------------------- /Chapter09/hashing-with-bcrypt/hash.js: -------------------------------------------------------------------------------- 1 | const password = process.argv[2]; 2 | const bcrypt = require("bcrypt"); 3 | const saltRounds = 10; 4 | 5 | bcrypt.hash(password, saltRounds, (err, hash) => { 6 | console.log(hash); 7 | }); 8 | -------------------------------------------------------------------------------- /Chapter09/hashing-with-bcrypt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hashing-with-bcrypt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter09/hashing-with-bcrypt/validate-password.js: -------------------------------------------------------------------------------- 1 | const password = process.argv[2]; 2 | const hash = process.argv[3]; 3 | const bcrypt = require("bcrypt"); 4 | 5 | bcrypt 6 | .compare(password, hash) 7 | .then((res) => { 8 | console.log(res); 9 | }) 10 | .catch((err) => console.error(err.message)); 11 | -------------------------------------------------------------------------------- /Chapter09/http-app/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const server = http.createServer((req, res) => { 4 | secureHeaders(res); 5 | res.end("Hello World!"); 6 | }); 7 | 8 | const secureHeaders = (res) => { 9 | res.setHeader("X-DNS-Prefetch-Control", "off"); 10 | res.setHeader("Expect-CT", "max-age=0"); 11 | res.setHeader("X-Frame-Options", "SAMEORIGIN"); 12 | res.setHeader("X-Download-Options", "noopen"); 13 | res.setHeader("X-Content-Type-Options", "nosniff"); 14 | res.setHeader("X-Permitted-Cross-Domain-Policies", "none"); 15 | res.setHeader("Referrer-Policy", "no-referrer"); 16 | res.setHeader("X-XSS-Protection", "1; mode=block"); 17 | }; 18 | 19 | server.listen(3000, () => { 20 | console.log("Server listening on port 3000"); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter09/json-pollution/fixed-server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const { STATUS_CODES } = http; 3 | 4 | const Ajv = require("ajv"); 5 | const ajv = new Ajv(); 6 | const schema = { 7 | title: "Greeting", 8 | properties: { 9 | msg: { type: "string" }, 10 | name: { type: "string" }, 11 | }, 12 | additionalProperties: false, 13 | required: ["msg"], 14 | }; 15 | const validate = ajv.compile(schema); 16 | 17 | const server = http.createServer((req, res) => { 18 | if (req.method === "POST" && req.url === "/") { 19 | greeting(req, res); 20 | return; 21 | } 22 | 23 | res.statusCode = 404; 24 | res.end(STATUS_CODES[res.statusCode]); 25 | }); 26 | 27 | greeting = (req, res) => { 28 | let data = ""; 29 | req.on("data", (chunk) => (data += chunk)); 30 | req.on("end", () => { 31 | try { 32 | data = JSON.parse(data); 33 | } catch (e) { 34 | res.end('{"msg": ""}'); 35 | return; 36 | } 37 | 38 | if (!validate(data, schema)) { 39 | res.end(""); 40 | return; 41 | } 42 | 43 | if (data.hasOwnProperty("name")) { 44 | res.end(`${data.msg} ${data.name}`); 45 | } else { 46 | res.end(data.msg); 47 | } 48 | }); 49 | }; 50 | 51 | server.listen(3000, () => { 52 | console.log("Server listening on port 3000"); 53 | }); 54 | -------------------------------------------------------------------------------- /Chapter09/json-pollution/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const { STATUS_CODES } = http; 3 | 4 | const server = http.createServer((req, res) => { 5 | if (req.method === "POST" && req.url === "/") { 6 | greeting(req, res); 7 | return; 8 | } 9 | 10 | res.statusCode = 404; 11 | res.end(STATUS_CODES[res.statusCode]); 12 | }); 13 | 14 | greeting = (req, res) => { 15 | let data = ""; 16 | req.on("data", (chunk) => (data += chunk)); 17 | req.on("end", () => { 18 | try { 19 | data = JSON.parse(data); 20 | } catch (e) { 21 | res.end('{"msg": ""}'); 22 | return; 23 | } 24 | 25 | if (data.hasOwnProperty("name")) { 26 | res.end(`${data.msg} ${data.name}`); 27 | } else { 28 | res.end(data.msg); 29 | } 30 | }); 31 | }; 32 | 33 | server.listen(3000, () => { 34 | console.log("Server listening on port 3000"); 35 | }); 36 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var cookieParser = require('cookie-parser'); 4 | var logger = require('morgan'); 5 | 6 | var indexRouter = require('./routes/index'); 7 | var usersRouter = require('./routes/users'); 8 | 9 | var app = express(); 10 | 11 | app.use(logger('dev')); 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: false })); 14 | app.use(cookieParser()); 15 | app.use(express.static(path.join(__dirname, 'public'))); 16 | 17 | app.use('/', indexRouter); 18 | app.use('/users', usersRouter); 19 | 20 | module.exports = app; 21 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarking-http", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "express": "~4.16.1", 12 | "morgan": "~1.9.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/post-server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const bodyParser = require("body-parser"); 4 | 5 | app.use(bodyParser.json()); 6 | app.use(bodyParser.urlencoded({ extended: false })); 7 | 8 | app.post("/", (req, res) => { 9 | res.send(req.body); 10 | }); 11 | 12 | app.listen(3000, () => { 13 | console.log("Server listening on port 3000"); 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Express 5 | 6 | 7 | 8 | 9 |

Express

10 |

Welcome to Express

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-http/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'jade'); 15 | 16 | app.use(logger('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: false })); 19 | app.use(cookieParser()); 20 | app.use(express.static(path.join(__dirname, 'public'))); 21 | 22 | app.use('/', indexRouter); 23 | app.use('/users', usersRouter); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | next(createError(404)); 28 | }); 29 | 30 | // error handler 31 | app.use(function(err, req, res, next) { 32 | // set locals, only providing error in development 33 | res.locals.message = err.message; 34 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 35 | 36 | // render the error page 37 | res.status(err.status || 500); 38 | res.render('error'); 39 | }); 40 | 41 | module.exports = app; 42 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarking-views", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "express": "~4.16.1", 12 | "http-errors": "~1.6.3", 13 | "jade": "~1.11.0", 14 | "morgan": "~1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /Chapter10/benchmarking-views/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'jade'); 15 | 16 | app.use(logger('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: false })); 19 | app.use(cookieParser()); 20 | app.use(express.static(path.join(__dirname, 'public'))); 21 | 22 | app.use('/', indexRouter); 23 | app.use('/users', usersRouter); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | next(createError(404)); 28 | }); 29 | 30 | // error handler 31 | app.use(function(err, req, res, next) { 32 | // set locals, only providing error in development 33 | res.locals.message = err.message; 34 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 35 | 36 | // render the error page 37 | res.status(err.status || 500); 38 | res.render('error'); 39 | }); 40 | 41 | module.exports = app; 42 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flamegraph-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "express": "~4.16.1", 12 | "http-errors": "~1.6.3", 13 | "jade": "~1.11.0", 14 | "morgan": "~1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /Chapter10/flamegraph-app/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /Chapter10/optimize-async/calculate-average.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require("mongodb").MongoClient; 2 | const URL = "mongodb://localhost:27017/"; 3 | 4 | MongoClient.connect(URL, { useUnifiedTopology: true }, (err, client) => { 5 | if (err) throw err; 6 | 7 | const db = client.db("data"); 8 | const values = db.collection("values"); 9 | const averages = db.collection("averages"); 10 | 11 | values.find({}).toArray((err, data) => { 12 | if (err) throw err; 13 | 14 | // Calculate average 15 | const average = 16 | data.reduce((accumulator, value) => accumulator + value.value, 0) / 17 | data.length; 18 | 19 | averages.find({}).toArray((err) => { 20 | if (err) throw err; 21 | 22 | averages.insertOne({ value: average }, (err) => { 23 | if (err) throw err; 24 | console.log("Stored average in database."); 25 | client.close(); 26 | }); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /Chapter10/optimize-async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimize-async", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1", 14 | "mongodb": "^3.6.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter10/optimize-async/server-no-processing.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require("mongodb").MongoClient; 2 | const URL = "mongodb://localhost:27017/"; 3 | 4 | const express = require("express"); 5 | const app = express(); 6 | 7 | MongoClient.connect(URL, { useUnifiedTopology: true }, (err, client) => { 8 | if (err) throw err; 9 | 10 | const db = client.db("data"); 11 | const average = db.collection("averages"); 12 | 13 | app.get("/", (req, res) => { 14 | average.findOne({}, (err, data) => { 15 | if (err) throw err; 16 | res.send(`Average of all values is ${data.value}.`); 17 | }); 18 | }); 19 | app.listen(3000); 20 | }); 21 | -------------------------------------------------------------------------------- /Chapter10/optimize-async/server.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require("mongodb").MongoClient; 2 | const URL = "mongodb://localhost:27017/"; 3 | 4 | const express = require("express"); 5 | const app = express(); 6 | 7 | MongoClient.connect(URL, { useUnifiedTopology: true }, (err, client) => { 8 | if (err) throw err; 9 | const db = client.db("data"); 10 | const values = db.collection("values"); 11 | 12 | app.get("/", (req, res) => { 13 | values.find({}).toArray(function sum(err, data) { 14 | if (err) { 15 | res.send(err); 16 | return; 17 | } 18 | 19 | // Calculate average 20 | const average = 21 | data.reduce((accumulator, value) => accumulator + value.value, 0) / 22 | data.length; 23 | res.send(`Average of all values is ${average}.`); 24 | }); 25 | }); 26 | app.listen(3000); 27 | }); 28 | -------------------------------------------------------------------------------- /Chapter10/optimize-async/values.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require("mongodb").MongoClient; 2 | const URL = "mongodb://localhost:27017/"; 3 | 4 | let values = []; 5 | const numberOfValues = 1000; 6 | 7 | let count = 0; 8 | for (count; count < numberOfValues; count++) { 9 | values.push({ value: Math.round(Math.random() * 100000) }); 10 | } 11 | 12 | MongoClient.connect(URL, { useUnifiedTopology: true }, (err, client) => { 13 | if (err) throw err; 14 | 15 | const db = client.db("data"); 16 | 17 | db.collection("values").insertMany(values, (err) => { 18 | if (err) throw err; 19 | console.log(`Added ${numberOfValues} random values.`); 20 | client.close(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /Chapter10/optimize-sync/benchmark.js: -------------------------------------------------------------------------------- 1 | const benchmark = require("benchmark"); 2 | const slow = require("./slow"); 3 | const loop = require("./loop"); 4 | const suite = new benchmark.Suite(); 5 | const maxNumber = 100; // number to pass through to sumOfSquares() 6 | 7 | suite.add("slow", function () { 8 | slow(maxNumber); 9 | }); 10 | 11 | suite.add("loop", function () { 12 | loop(maxNumber); 13 | }); 14 | 15 | suite.on("complete", printResults); 16 | suite.run(); 17 | 18 | function printResults() { 19 | this.forEach((benchmark) => { 20 | console.log(benchmark.toString()); 21 | }); 22 | console.log("Fastest implementation is", this.filter("fastest")[0].name); 23 | } 24 | -------------------------------------------------------------------------------- /Chapter10/optimize-sync/loop.js: -------------------------------------------------------------------------------- 1 | function sumOfSquares(maxNumber) { 2 | let i = 0; 3 | let sum = 0; 4 | for (i; i <= maxNumber; i++) { 5 | sum += i ** 2; 6 | } 7 | return sum; 8 | } 9 | 10 | module.exports = sumOfSquares; 11 | -------------------------------------------------------------------------------- /Chapter10/optimize-sync/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimize-sync", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "benchmark": { 8 | "version": "2.1.4", 9 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", 10 | "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", 11 | "requires": { 12 | "lodash": "^4.17.4", 13 | "platform": "^1.3.3" 14 | } 15 | }, 16 | "lodash": { 17 | "version": "4.17.20", 18 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 19 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 20 | }, 21 | "platform": { 22 | "version": "1.3.6", 23 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", 24 | "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter10/optimize-sync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimize-sync", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "benchmark": "^2.1.4" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter10/optimize-sync/slow.js: -------------------------------------------------------------------------------- 1 | function sumOfSquares(maxNumber) { 2 | const array = Array.from(Array(maxNumber + 1).keys()); 3 | 4 | return array 5 | .map((number) => { 6 | return number ** 2; 7 | }) 8 | .reduce((accumulator, item) => { 9 | return accumulator + item; 10 | }); 11 | } 12 | 13 | module.exports = sumOfSquares; 14 | -------------------------------------------------------------------------------- /Chapter10/profiling-memory/leaky-server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const server = http.createServer((req, res) => { 4 | server.on("connection", () => { 5 | console.log("connected"); 6 | }); 7 | res.end("Hello World!"); 8 | }); 9 | 10 | server.listen(3000, () => { 11 | console.log("Server listening on port 3000"); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter10/profiling-memory/max-listeners.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const server = http.createServer((req, res) => { 4 | server.setMaxListeners(1); 5 | 6 | server.on("connection", () => { 7 | console.log("connected"); 8 | }); 9 | res.end("Hello World!"); 10 | }); 11 | 12 | server.listen(3000, () => { 13 | console.log("Server listening on port 3000"); 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter10/profiling-memory/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | 3 | const server = http.createServer((req, res) => { 4 | res.end("Hello World!"); 5 | }); 6 | 7 | server.on("connection", () => { 8 | console.log("connected"); 9 | }); 10 | 11 | server.listen(3000, () => { 12 | console.log("Server listening on port 3000"); 13 | }); 14 | -------------------------------------------------------------------------------- /Chapter10/worker-app/fibonacci-worker.js: -------------------------------------------------------------------------------- 1 | const { 2 | Worker, 3 | isMainThread, 4 | parentPort, 5 | workerData, 6 | } = require("worker_threads"); 7 | 8 | const n = 10; 9 | // Fibonacci calculator 10 | const fibonacci = (n) => { 11 | let a = 0, b = 1, next = 1, i = 2; 12 | for (i; i <= n; i++) { 13 | next = a + b; 14 | a = b; 15 | b = next; 16 | } 17 | return next; 18 | }; 19 | 20 | if (isMainThread) { 21 | // Main thread code 22 | const worker = new Worker(__filename, { 23 | workerData: n, 24 | }); 25 | worker.on("message", (msg) => { 26 | console.log(`The Fibonacci number at position ${n} is ${msg}`); 27 | }); 28 | console.log("..."); 29 | } else { 30 | // Worker code 31 | parentPort.postMessage(fibonacci(workerData)); 32 | } 33 | -------------------------------------------------------------------------------- /Chapter10/worker-app/fibonacci.js: -------------------------------------------------------------------------------- 1 | const n = 10; 2 | // Fibonacci calculator 3 | const fibonacci = (n) => { 4 | let a = 0, b = 1, next = 1, i = 2; 5 | for (i; i <= n; i++) { 6 | next = a + b; 7 | a = b; 8 | b = next; 9 | } 10 | console.log(`The Fibonacci number at position ${n} is ${next}`); 11 | }; 12 | 13 | fibonacci(n); 14 | console.log("..."); 15 | -------------------------------------------------------------------------------- /Chapter10/worker-app/hello-worker.js: -------------------------------------------------------------------------------- 1 | const { 2 | Worker, 3 | isMainThread, 4 | parentPort, 5 | workerData, 6 | } = require("worker_threads"); 7 | 8 | if (isMainThread) { 9 | // Main thread code 10 | const worker = new Worker(__filename, { 11 | workerData: "Beth", 12 | }); 13 | worker.on("message", (msg) => { 14 | console.log(msg); 15 | }); 16 | } else { 17 | // Worker code 18 | const greeting = `Hello ${workerData}!`; 19 | parentPort.postMessage(greeting); 20 | } 21 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/app.js: -------------------------------------------------------------------------------- 1 | var createError = require("http-errors"); 2 | var express = require("express"); 3 | var path = require("path"); 4 | var cookieParser = require("cookie-parser"); 5 | var logger = require("morgan"); 6 | 7 | var indexRouter = require("./routes/index"); 8 | var usersRouter = require("./routes/users"); 9 | var inventoryRouter = require("./routes/inventory"); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set("views", path.join(__dirname, "views")); 15 | app.set("view engine", "ejs"); 16 | 17 | app.use(logger("dev")); 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: false })); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, "public"))); 22 | 23 | app.use("/", indexRouter); 24 | app.use("/users", usersRouter); 25 | app.use("/inventory", inventoryRouter); 26 | 27 | // catch 404 and forward to error handler 28 | app.use(function (req, res, next) { 29 | next(createError(404)); 30 | }); 31 | 32 | // error handler 33 | app.use(function (err, req, res, next) { 34 | // set locals, only providing error in development 35 | res.locals.message = err.message; 36 | res.locals.error = req.app.get("env") === "development" ? err : {}; 37 | 38 | // render the error page 39 | res.status(err.status || 500); 40 | res.render("error"); 41 | }); 42 | 43 | module.exports = app; 44 | 45 | process.on("unhandledRejection", (reason, promise) => { 46 | console.log("Unhandled Rejection at:", promise, "reason:", reason); 47 | process.exit(1); 48 | }); 49 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookstore-web-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "http-errors": "~1.6.3", 14 | "morgan": "~1.9.1", 15 | "node-fetch": "^2.6.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/routes/inventory.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const fetch = require("node-fetch"); 3 | 4 | const router = Router(); 5 | 6 | router.get("/", function (req, res) { 7 | fetch("http://localhost:3000/books") 8 | .then((res) => res.json()) 9 | .then((json) => 10 | res.render("inventory", { 11 | books: json, 12 | }) 13 | ) 14 | .catch((error) => { 15 | res.render("error", { 16 | error: error, 17 | message: error.message, 18 | }); 19 | }); 20 | }); 21 | 22 | router.post("/add", function (req, res) { 23 | console.log(req.body); 24 | 25 | fetch("http://localhost:3000/books", { 26 | method: "POST", 27 | body: JSON.stringify(req.body), 28 | headers: { "Content-Type": "application/json" }, 29 | }) 30 | .then(res.redirect("/inventory")) 31 | .catch((err) => { 32 | throw err; 33 | }); 34 | }); 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter11/bookstore-error-handling/views/inventory.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Book Inventory 5 | 6 | 7 | 8 |

Book Inventory

9 | 14 |

Add Book:

15 |
16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | var inventoryRouter = require('./routes/inventory'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | app.use(logger('dev')); 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: false })); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, 'public'))); 22 | 23 | app.use('/', indexRouter); 24 | app.use('/users', usersRouter); 25 | app.use('/inventory', inventoryRouter); 26 | 27 | // catch 404 and forward to error handler 28 | app.use(function(req, res, next) { 29 | next(createError(404)); 30 | }); 31 | 32 | // error handler 33 | app.use(function(err, req, res, next) { 34 | // set locals, only providing error in development 35 | res.locals.message = err.message; 36 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 37 | 38 | // render the error page 39 | res.status(err.status || 500); 40 | res.render('error'); 41 | }); 42 | 43 | module.exports = app; 44 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookstore-web-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "http-errors": "~1.6.3", 14 | "morgan": "~1.9.1", 15 | "node-fetch": "^2.6.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/routes/inventory.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const fetch = require("node-fetch"); 3 | 4 | const router = Router(); 5 | 6 | router.get("/", function (req, res) { 7 | fetch("http://localhost:3000/books") 8 | .then((res) => res.json()) 9 | .then((json) => 10 | res.render("inventory", { 11 | books: json, 12 | }) 13 | ); 14 | }); 15 | 16 | router.post("/add", function (req, res) { 17 | console.log(req.body); 18 | 19 | fetch("http://localhost:3000/books", { 20 | method: "POST", 21 | body: JSON.stringify(req.body), 22 | headers: { "Content-Type": "application/json" }, 23 | }) 24 | .then(res.redirect("/inventory")) 25 | .catch((err) => { 26 | throw err; 27 | }); 28 | }); 29 | 30 | module.exports = router; 31 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter11/bookstore-web-app/views/inventory.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Book Inventory 5 | 6 | 7 | 8 |

Book Inventory

9 | 14 |

Add Book:

15 |
16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | node_modules 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # 0x 40 | profile-* 41 | 42 | # mac files 43 | .DS_Store 44 | 45 | # vim swap files 46 | *.swp 47 | 48 | # webstorm 49 | .idea 50 | 51 | # vscode 52 | .vscode 53 | *code-workspace 54 | 55 | # clinic 56 | profile* 57 | *clinic* 58 | *flamegraph* 59 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR "/app" 4 | 5 | RUN apt-get update \ 6 | && apt-get dist-upgrade -y \ 7 | && apt-get clean \ 8 | && echo 'Finished installing dependencies' 9 | 10 | COPY package*.json ./ 11 | 12 | RUN npm install --production 13 | 14 | COPY . /app 15 | 16 | ENV PORT 3000 17 | 18 | EXPOSE 3000 19 | 20 | USER node 21 | 22 | CMD ["npm", "start"] 23 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/Dockerfile-run: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR "/app" 4 | 5 | RUN apt-get update \ 6 | && apt-get dist-upgrade -y \ 7 | && apt-get clean \ 8 | && echo 'Finished installing dependencies' 9 | 10 | COPY package*.json ./ 11 | 12 | RUN npm install --production 13 | 14 | FROM node:14-slim 15 | 16 | WORKDIR "/app" 17 | 18 | RUN apt-get update \ 19 | && apt-get dist-upgrade -y \ 20 | && apt-get clean \ 21 | && echo 'Finished installing dependencies' 22 | 23 | COPY --from=0 /app/node_modules /app/node_modules 24 | COPY . /app 25 | 26 | ENV NODE_ENV production 27 | ENV PORT 3000 28 | USER node 29 | EXPOSE 3000 30 | 31 | CMD ["npm", "start"] 32 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const AutoLoad = require('fastify-autoload') 5 | 6 | module.exports = async function (fastify, opts) { 7 | // Place here your custom code! 8 | 9 | // Do not touch the following lines 10 | 11 | // This loads all plugins defined in plugins 12 | // those should be support plugins that are reused 13 | // through your application 14 | fastify.register(AutoLoad, { 15 | dir: path.join(__dirname, 'plugins'), 16 | options: Object.assign({}, opts) 17 | }) 18 | 19 | // This loads all plugins defined in routes 20 | // define your routes in one of these 21 | fastify.register(AutoLoad, { 22 | dir: path.join(__dirname, 'routes'), 23 | options: Object.assign({}, opts) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/deployment/fastify-app-svc.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: fastify-app-svc 5 | labels: 6 | run: fastify 7 | spec: 8 | selector: 9 | app: fastify 10 | ports: 11 | - protocol: TCP 12 | port: 3000 13 | targetPort: 3000 14 | type: NodePort 15 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/deployment/fastify-app.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: fastify-app 5 | labels: 6 | app: fastify 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: fastify 12 | template: 13 | metadata: 14 | labels: 15 | app: fastify 16 | spec: 17 | containers: 18 | - name: fastify-app 19 | image: fastify-microservice:latest 20 | imagePullPolicy: Never 21 | ports: 22 | - containerPort: 3000 23 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-microservice", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "tap test/**/*.test.js", 11 | "start": "fastify start -l info app.js", 12 | "dev": "fastify start -w -l info -P app.js" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "fastify": "^3.0.0", 19 | "fastify-plugin": "^3.0.0", 20 | "fastify-autoload": "^3.0.2", 21 | "fastify-cli": "^2.5.1" 22 | }, 23 | "devDependencies": { 24 | "tap": "^14.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins Folder 2 | 3 | Plugins define behavior that is common to all the routes in your 4 | application. Authentication, caching, templates, and all the other cross 5 | cutting concerns should be handled by plugins placed in this folder. 6 | 7 | Files in this folder are typically defined through the 8 | [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module, 9 | making them non-encapsulated. They can define decorators and set hooks 10 | that will then be used in the rest of your application. 11 | 12 | Check out: 13 | 14 | * [The hitchhiker's guide to plugins](https://www.fastify.io/docs/latest/Plugins-Guide/) 15 | * [Fastify decorators](https://www.fastify.io/docs/latest/Decorators/). 16 | * [Fastify lifecycle](https://www.fastify.io/docs/latest/Lifecycle/). 17 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/plugins/support.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fp = require('fastify-plugin') 4 | 5 | // the use of fastify-plugin is required to be able 6 | // to export the decorators to the outer scope 7 | 8 | module.exports = fp(async function (fastify, opts) { 9 | fastify.decorate('someSupport', function () { 10 | return 'hugs' 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/routes/README.md: -------------------------------------------------------------------------------- 1 | # Services Folder 2 | 3 | Services define routes within your application. Fastify provides an 4 | easy path to a microservice architecture, in the future you might want 5 | to independently deploy some of those. 6 | 7 | In this folder you should define all the services that define the routes 8 | of your web application. 9 | Each service is a [Fastify 10 | plugin](https://www.fastify.io/docs/latest/Plugins/), it is 11 | encapsulated (it can have its own independent plugins) and it is 12 | typically stored in a file; be careful to group your routes logically, 13 | e.g. all `/users` routes in a `users.js` file. We have added 14 | a `root.js` file for you with a '/' root added. 15 | 16 | If a single file become too large, create a folder and add a `index.js` file there: 17 | this file must be a Fastify plugin, and it will be loaded automatically 18 | by the application. You can now add as many files as you want inside that folder. 19 | In this way you can create complex services within a single monolith, 20 | and eventually extract them. 21 | 22 | If you need to share functionality between services, place that 23 | functionality into the `plugins` folder, and share it via 24 | [decorators](https://www.fastify.io/docs/latest/Decorators/). 25 | 26 | If you're a bit confused about using `async/await` to write services, you would 27 | better take a look at [Promise resolution](https://www.fastify.io/docs/latest/Routes/#promise-resolution) for more details. 28 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/routes/example/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (fastify, opts) { 4 | fastify.get('/', async function (request, reply) { 5 | return 'this is an example' 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/routes/root.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async function (fastify, opts) { 4 | fastify.get('/', async function (request, reply) { 5 | return { root: true } 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/test/helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file contains code that we reuse 4 | // between our tests. 5 | 6 | const Fastify = require('fastify') 7 | const fp = require('fastify-plugin') 8 | const App = require('../app') 9 | 10 | // Fill in this config with all the configurations 11 | // needed for testing the application 12 | function config () { 13 | return {} 14 | } 15 | 16 | // automatically build and tear down our instance 17 | function build (t) { 18 | const app = Fastify() 19 | 20 | // fastify-plugin ensures that all decorators 21 | // are exposed for testing purposes, this is 22 | // different from the production setup 23 | app.register(fp(App), config()) 24 | 25 | // tear down our app after we are done 26 | t.tearDown(app.close.bind(app)) 27 | 28 | return app 29 | } 30 | 31 | module.exports = { 32 | config, 33 | build 34 | } 35 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/test/plugins/support.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const Fastify = require('fastify') 5 | const Support = require('../../plugins/support') 6 | 7 | test('support works standalone', async (t) => { 8 | const fastify = Fastify() 9 | fastify.register(Support) 10 | 11 | await fastify.ready() 12 | t.equal(fastify.someSupport(), 'hugs') 13 | }) 14 | 15 | // You can also use plugin with opts in fastify v2 16 | // 17 | // test('support works standalone', (t) => { 18 | // t.plan(2) 19 | // const fastify = Fastify() 20 | // fastify.register(Support) 21 | // 22 | // fastify.ready((err) => { 23 | // t.error(err) 24 | // t.equal(fastify.someSupport(), 'hugs') 25 | // }) 26 | // }) 27 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/test/routes/example.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const { build } = require('../helper') 5 | 6 | test('example is loaded', async (t) => { 7 | const app = build(t) 8 | 9 | const res = await app.inject({ 10 | url: '/example' 11 | }) 12 | t.equal(res.payload, 'this is an example') 13 | }) 14 | 15 | // inject callback style: 16 | // 17 | // test('example is loaded', (t) => { 18 | // t.plan(2) 19 | // const app = build(t) 20 | // 21 | // app.inject({ 22 | // url: '/example' 23 | // }, (err, res) => { 24 | // t.error(err) 25 | // t.equal(res.payload, 'this is an example') 26 | // }) 27 | // }) 28 | -------------------------------------------------------------------------------- /Chapter11/fastify-microservice/test/routes/root.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('tap') 4 | const { build } = require('../helper') 5 | 6 | test('default root route', async (t) => { 7 | const app = build(t) 8 | 9 | const res = await app.inject({ 10 | url: '/' 11 | }) 12 | t.deepEqual(JSON.parse(res.payload), { root: true }) 13 | }) 14 | 15 | // inject callback style: 16 | // 17 | // test('default root route', (t) => { 18 | // t.plan(2) 19 | // const app = build(t) 20 | // 21 | // app.inject({ 22 | // url: '/' 23 | // }, (err, res) => { 24 | // t.error(err) 25 | // t.deepEqual(JSON.parse(res.payload), { root: true }) 26 | // }) 27 | // }) 28 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | /dist 4 | # Cache used by TypeScript's incremental build 5 | *.tsbuildinfo 6 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: '@loopback/eslint-config', 3 | }; 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Transpiled JavaScript files from Typescript 61 | /dist 62 | 63 | # Cache used by TypeScript's incremental build 64 | *.tsbuildinfo 65 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "recursive": true, 3 | "require": "source-map-support/register" 4 | } 5 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true 2 | scripts-prepend-node-path=true 3 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.json 3 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "trailingComma": "all", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [80], 3 | "editor.tabCompletion": "on", 4 | "editor.tabSize": 2, 5 | "editor.trimAutoWhitespace": true, 6 | "editor.formatOnSave": true, 7 | "editor.codeActionsOnSave": { 8 | "source.organizeImports": true, 9 | "source.fixAll.eslint": true 10 | }, 11 | 12 | "files.exclude": { 13 | "**/.DS_Store": true, 14 | "**/.git": true, 15 | "**/.hg": true, 16 | "**/.svn": true, 17 | "**/CVS": true, 18 | "dist": true, 19 | }, 20 | "files.insertFinalNewline": true, 21 | "files.trimTrailingWhitespace": true, 22 | 23 | "typescript.tsdk": "./node_modules/typescript/lib", 24 | "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, 25 | "typescript.preferences.quoteStyle": "single", 26 | "eslint.run": "onSave", 27 | "eslint.nodePath": "./node_modules", 28 | "eslint.validate": [ 29 | "javascript", 30 | "typescript" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Watch and Compile Project", 8 | "type": "shell", 9 | "command": "npm", 10 | "args": ["--silent", "run", "build:watch"], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | }, 15 | "problemMatcher": "$tsc-watch" 16 | }, 17 | { 18 | "label": "Build, Test and Lint", 19 | "type": "shell", 20 | "command": "npm", 21 | "args": ["--silent", "run", "test:dev"], 22 | "group": { 23 | "kind": "test", 24 | "isDefault": true 25 | }, 26 | "problemMatcher": ["$tsc", "$eslint-compact", "$eslint-stylish"] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@loopback/cli": { 3 | "version": "2.10.0", 4 | "packageManager": "npm" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Developer's Guide 2 | 3 | We use Visual Studio Code for developing LoopBack and recommend the same to our 4 | users. 5 | 6 | ## VSCode setup 7 | 8 | Install the following extensions: 9 | 10 | - [eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) 11 | - [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 12 | 13 | ## Development workflow 14 | 15 | ### Visual Studio Code 16 | 17 | 1. Start the build task (Cmd+Shift+B) to run TypeScript compiler in the 18 | background, watching and recompiling files as you change them. Compilation 19 | errors will be shown in the VSCode's "PROBLEMS" window. 20 | 21 | 2. Execute "Run Rest Task" from the Command Palette (Cmd+Shift+P) to re-run the 22 | test suite and lint the code for both programming and style errors. Linting 23 | errors will be shown in VSCode's "PROBLEMS" window. Failed tests are printed 24 | to terminal output only. 25 | 26 | ### Other editors/IDEs 27 | 28 | 1. Open a new terminal window/tab and start the continuous build process via 29 | `npm run build:watch`. It will run TypeScript compiler in watch mode, 30 | recompiling files as you change them. Any compilation errors will be printed 31 | to the terminal. 32 | 33 | 2. In your main terminal window/tab, run `npm run test:dev` to re-run the test 34 | suite and lint the code for both programming and style errors. You should run 35 | this command manually whenever you have new changes to test. Test failures 36 | and linter errors will be printed to the terminal. 37 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/Dockerfile: -------------------------------------------------------------------------------- 1 | # Check out https://hub.docker.com/_/node to select a new base image 2 | FROM node:10-slim 3 | 4 | # Set to a non-root built-in user `node` 5 | USER node 6 | 7 | # Create app directory (with user `node`) 8 | RUN mkdir -p /home/node/app 9 | 10 | WORKDIR /home/node/app 11 | 12 | # Install app dependencies 13 | # A wildcard is used to ensure both package.json AND package-lock.json are copied 14 | # where available (npm@5+) 15 | COPY --chown=node package*.json ./ 16 | 17 | RUN npm install 18 | 19 | # Bundle app source code 20 | COPY --chown=node . . 21 | 22 | RUN npm run build 23 | 24 | # Bind to all network interfaces so that it can be mapped to the host OS 25 | ENV HOST=0.0.0.0 PORT=3000 26 | 27 | EXPOSE ${PORT} 28 | CMD [ "node", "." ] 29 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/README.md: -------------------------------------------------------------------------------- 1 | # loopback-bookstore 2 | 3 | [![LoopBack](https://github.com/strongloop/loopback-next/raw/master/docs/site/imgs/branding/Powered-by-LoopBack-Badge-(blue)-@2x.png)](http://loopback.io/) 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/__tests__/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Please place your tests in this folder. 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/__tests__/acceptance/home-page.acceptance.ts: -------------------------------------------------------------------------------- 1 | import {Client} from '@loopback/testlab'; 2 | import {LoopbackBookstoreApplication} from '../..'; 3 | import {setupApplication} from './test-helper'; 4 | 5 | describe('HomePage', () => { 6 | let app: LoopbackBookstoreApplication; 7 | let client: Client; 8 | 9 | before('setupApplication', async () => { 10 | ({app, client} = await setupApplication()); 11 | }); 12 | 13 | after(async () => { 14 | await app.stop(); 15 | }); 16 | 17 | it('exposes a default home page', async () => { 18 | await client 19 | .get('/') 20 | .expect(200) 21 | .expect('Content-Type', /text\/html/); 22 | }); 23 | 24 | it('exposes self-hosted explorer', async () => { 25 | await client 26 | .get('/explorer/') 27 | .expect(200) 28 | .expect('Content-Type', /text\/html/) 29 | .expect(/LoopBack API Explorer/); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/__tests__/acceptance/ping.controller.acceptance.ts: -------------------------------------------------------------------------------- 1 | import {Client, expect} from '@loopback/testlab'; 2 | import {LoopbackBookstoreApplication} from '../..'; 3 | import {setupApplication} from './test-helper'; 4 | 5 | describe('PingController', () => { 6 | let app: LoopbackBookstoreApplication; 7 | let client: Client; 8 | 9 | before('setupApplication', async () => { 10 | ({app, client} = await setupApplication()); 11 | }); 12 | 13 | after(async () => { 14 | await app.stop(); 15 | }); 16 | 17 | it('invokes GET /ping', async () => { 18 | const res = await client.get('/ping?msg=world').expect(200); 19 | expect(res.body).to.containEql({greeting: 'Hello from LoopBack'}); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/__tests__/acceptance/test-helper.ts: -------------------------------------------------------------------------------- 1 | import {LoopbackBookstoreApplication} from '../..'; 2 | import { 3 | createRestAppClient, 4 | givenHttpServerConfig, 5 | Client, 6 | } from '@loopback/testlab'; 7 | 8 | export async function setupApplication(): Promise<AppWithClient> { 9 | const restConfig = givenHttpServerConfig({ 10 | // Customize the server configuration here. 11 | // Empty values (undefined, '') will be ignored by the helper. 12 | // 13 | // host: process.env.HOST, 14 | // port: +process.env.PORT, 15 | }); 16 | 17 | const app = new LoopbackBookstoreApplication({ 18 | rest: restConfig, 19 | }); 20 | 21 | await app.boot(); 22 | await app.start(); 23 | 24 | const client = createRestAppClient(app); 25 | 26 | return {app, client}; 27 | } 28 | 29 | export interface AppWithClient { 30 | app: LoopbackBookstoreApplication; 31 | client: Client; 32 | } 33 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/application.ts: -------------------------------------------------------------------------------- 1 | import {BootMixin} from '@loopback/boot'; 2 | import {ApplicationConfig} from '@loopback/core'; 3 | import { 4 | RestExplorerBindings, 5 | RestExplorerComponent, 6 | } from '@loopback/rest-explorer'; 7 | import {RepositoryMixin} from '@loopback/repository'; 8 | import {RestApplication} from '@loopback/rest'; 9 | import {ServiceMixin} from '@loopback/service-proxy'; 10 | import path from 'path'; 11 | import {MySequence} from './sequence'; 12 | 13 | export {ApplicationConfig}; 14 | 15 | export class LoopbackBookstoreApplication extends BootMixin( 16 | ServiceMixin(RepositoryMixin(RestApplication)), 17 | ) { 18 | constructor(options: ApplicationConfig = {}) { 19 | super(options); 20 | 21 | // Set up the custom sequence 22 | this.sequence(MySequence); 23 | 24 | // Set up default home page 25 | this.static('/', path.join(__dirname, '../public')); 26 | 27 | // Customize @loopback/rest-explorer configuration here 28 | this.configure(RestExplorerBindings.COMPONENT).to({ 29 | path: '/explorer', 30 | }); 31 | this.component(RestExplorerComponent); 32 | 33 | this.projectRoot = __dirname; 34 | // Customize @loopback/boot Booter Conventions here 35 | this.bootOptions = { 36 | controllers: { 37 | // Customize ControllerBooter Conventions here 38 | dirs: ['controllers'], 39 | extensions: ['.controller.js'], 40 | nested: true, 41 | }, 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/controllers/README.md: -------------------------------------------------------------------------------- 1 | # Controllers 2 | 3 | This directory contains source files for the controllers exported by this app. 4 | 5 | To add a new empty controller, type in `lb4 controller [<name>]` from the 6 | command-line of your application's root directory. 7 | 8 | For more information, please visit 9 | [Controller generator](http://loopback.io/doc/en/lb4/Controller-generator.html). 10 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ping.controller'; 2 | export * from './books.controller'; 3 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/controllers/ping.controller.ts: -------------------------------------------------------------------------------- 1 | import {Request, RestBindings, get, ResponseObject} from '@loopback/rest'; 2 | import {inject} from '@loopback/core'; 3 | 4 | /** 5 | * OpenAPI response for ping() 6 | */ 7 | const PING_RESPONSE: ResponseObject = { 8 | description: 'Ping Response', 9 | content: { 10 | 'application/json': { 11 | schema: { 12 | type: 'object', 13 | title: 'PingResponse', 14 | properties: { 15 | greeting: {type: 'string'}, 16 | date: {type: 'string'}, 17 | url: {type: 'string'}, 18 | headers: { 19 | type: 'object', 20 | properties: { 21 | 'Content-Type': {type: 'string'}, 22 | }, 23 | additionalProperties: true, 24 | }, 25 | }, 26 | }, 27 | }, 28 | }, 29 | }; 30 | 31 | /** 32 | * A simple controller to bounce back http requests 33 | */ 34 | export class PingController { 35 | constructor(@inject(RestBindings.Http.REQUEST) private req: Request) {} 36 | 37 | // Map to `GET /ping` 38 | @get('/ping', { 39 | responses: { 40 | '200': PING_RESPONSE, 41 | }, 42 | }) 43 | ping(): object { 44 | // Reply with a greeting, the current time, the url, and request headers 45 | return { 46 | greeting: 'Hello from LoopBack', 47 | date: new Date(), 48 | url: this.req.url, 49 | headers: Object.assign({}, this.req.headers), 50 | }; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/datasources/README.md: -------------------------------------------------------------------------------- 1 | # Datasources 2 | 3 | This directory contains config for datasources used by this app. 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/datasources/index.ts: -------------------------------------------------------------------------------- 1 | export * from './local.datasource'; 2 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/datasources/local.datasource.ts: -------------------------------------------------------------------------------- 1 | import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core'; 2 | import {juggler} from '@loopback/repository'; 3 | 4 | const config = { 5 | name: 'local', 6 | connector: 'memory', 7 | localStorage: '', 8 | file: '' 9 | }; 10 | 11 | // Observe application's life cycle to disconnect the datasource when 12 | // application is stopped. This allows the application to be shut down 13 | // gracefully. The `stop()` method is inherited from `juggler.DataSource`. 14 | // Learn more at https://loopback.io/doc/en/lb4/Life-cycle.html 15 | @lifeCycleObserver('datasource') 16 | export class LocalDataSource extends juggler.DataSource 17 | implements LifeCycleObserver { 18 | static dataSourceName = 'local'; 19 | static readonly defaultConfig = config; 20 | 21 | constructor( 22 | @inject('datasources.config.local', {optional: true}) 23 | dsConfig: object = config, 24 | ) { 25 | super(dsConfig); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/index.ts: -------------------------------------------------------------------------------- 1 | import {ApplicationConfig, LoopbackBookstoreApplication} from './application'; 2 | 3 | export * from './application'; 4 | 5 | export async function main(options: ApplicationConfig = {}) { 6 | const app = new LoopbackBookstoreApplication(options); 7 | await app.boot(); 8 | await app.start(); 9 | 10 | const url = app.restServer.url; 11 | console.log(`Server is running at ${url}`); 12 | console.log(`Try ${url}/ping`); 13 | 14 | return app; 15 | } 16 | 17 | if (require.main === module) { 18 | // Run the application 19 | const config = { 20 | rest: { 21 | port: +(process.env.PORT ?? 3000), 22 | host: process.env.HOST, 23 | // The `gracePeriodForClose` provides a graceful close for http/https 24 | // servers with keep-alive clients. The default value is `Infinity` 25 | // (don't force-close). If you want to immediately destroy all sockets 26 | // upon stop, set its value to `0`. 27 | // See https://www.npmjs.com/package/stoppable 28 | gracePeriodForClose: 5000, // 5 seconds 29 | openApiSpec: { 30 | // useful when used with OpenAPI-to-GraphQL to locate your application 31 | setServersFromRequest: true, 32 | }, 33 | }, 34 | }; 35 | main(config).catch(err => { 36 | console.error('Cannot start the application.', err); 37 | process.exit(1); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/migrate.ts: -------------------------------------------------------------------------------- 1 | import {LoopbackBookstoreApplication} from './application'; 2 | 3 | export async function migrate(args: string[]) { 4 | const existingSchema = args.includes('--rebuild') ? 'drop' : 'alter'; 5 | console.log('Migrating schemas (%s existing schema)', existingSchema); 6 | 7 | const app = new LoopbackBookstoreApplication(); 8 | await app.boot(); 9 | await app.migrateSchema({existingSchema}); 10 | 11 | // Connectors usually keep a pool of opened connections, 12 | // this keeps the process running even after all work is done. 13 | // We need to exit explicitly. 14 | process.exit(0); 15 | } 16 | 17 | migrate(process.argv).catch(err => { 18 | console.error('Cannot migrate database schema', err); 19 | process.exit(1); 20 | }); 21 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/models/README.md: -------------------------------------------------------------------------------- 1 | # Models 2 | 3 | This directory contains code for models provided by this app. 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/models/book.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity, model, property} from '@loopback/repository'; 2 | 3 | @model() 4 | export class Book extends Entity { 5 | @property({ 6 | type: 'number', 7 | id: true, 8 | generated: true, 9 | }) 10 | id?: number; 11 | 12 | @property({ 13 | type: 'string', 14 | required: true, 15 | }) 16 | title: string; 17 | 18 | @property({ 19 | type: 'string', 20 | required: true, 21 | }) 22 | author: string; 23 | 24 | 25 | constructor(data?: Partial<Book>) { 26 | super(data); 27 | } 28 | } 29 | 30 | export interface BookRelations { 31 | // describe navigational properties here 32 | } 33 | 34 | export type BookWithRelations = Book & BookRelations; 35 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './book.model'; 2 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/openapi-spec.ts: -------------------------------------------------------------------------------- 1 | import {ApplicationConfig} from '@loopback/core'; 2 | import {LoopbackBookstoreApplication} from './application'; 3 | 4 | /** 5 | * Export the OpenAPI spec from the application 6 | */ 7 | async function exportOpenApiSpec(): Promise<void> { 8 | const config: ApplicationConfig = { 9 | rest: { 10 | port: +(process.env.PORT ?? 3000), 11 | host: process.env.HOST ?? 'localhost', 12 | }, 13 | }; 14 | const outFile = process.argv[2] ?? ''; 15 | const app = new LoopbackBookstoreApplication(config); 16 | await app.boot(); 17 | await app.exportOpenApiSpec(outFile); 18 | } 19 | 20 | exportOpenApiSpec().catch(err => { 21 | console.error('Fail to export OpenAPI spec from the application.', err); 22 | process.exit(1); 23 | }); 24 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/repositories/README.md: -------------------------------------------------------------------------------- 1 | # Repositories 2 | 3 | This directory contains code for repositories provided by this app. 4 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/repositories/book.repository.ts: -------------------------------------------------------------------------------- 1 | import {DefaultCrudRepository} from '@loopback/repository'; 2 | import {Book, BookRelations} from '../models'; 3 | import {LocalDataSource} from '../datasources'; 4 | import {inject} from '@loopback/core'; 5 | 6 | export class BookRepository extends DefaultCrudRepository< 7 | Book, 8 | typeof Book.prototype.id, 9 | BookRelations 10 | > { 11 | constructor( 12 | @inject('datasources.local') dataSource: LocalDataSource, 13 | ) { 14 | super(Book, dataSource); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/repositories/index.ts: -------------------------------------------------------------------------------- 1 | export * from './book.repository'; 2 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/src/sequence.ts: -------------------------------------------------------------------------------- 1 | import {inject} from '@loopback/core'; 2 | import { 3 | FindRoute, 4 | InvokeMethod, 5 | InvokeMiddleware, 6 | ParseParams, 7 | Reject, 8 | RequestContext, 9 | RestBindings, 10 | Send, 11 | SequenceHandler, 12 | } from '@loopback/rest'; 13 | 14 | const SequenceActions = RestBindings.SequenceActions; 15 | 16 | export class MySequence implements SequenceHandler { 17 | /** 18 | * Optional invoker for registered middleware in a chain. 19 | * To be injected via SequenceActions.INVOKE_MIDDLEWARE. 20 | */ 21 | @inject(SequenceActions.INVOKE_MIDDLEWARE, {optional: true}) 22 | protected invokeMiddleware: InvokeMiddleware = () => false; 23 | 24 | constructor( 25 | @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute, 26 | @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams, 27 | @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod, 28 | @inject(SequenceActions.SEND) public send: Send, 29 | @inject(SequenceActions.REJECT) public reject: Reject, 30 | ) {} 31 | 32 | async handle(context: RequestContext) { 33 | try { 34 | const {request, response} = context; 35 | const finished = await this.invokeMiddleware(context); 36 | if (finished) return; 37 | const route = this.findRoute(request); 38 | const args = await this.parseParams(request, route); 39 | const result = await this.invoke(route, args); 40 | this.send(response, result); 41 | } catch (err) { 42 | this.reject(context, err); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter11/loopback-bookstore/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "extends": "@loopback/build/config/tsconfig.common.json", 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "rootDir": "src" 7 | }, 8 | "include": ["src"] 9 | } 10 | -------------------------------------------------------------------------------- /Chapter12/core-debug-logs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "core-debug-logs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/core-debug-logs/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => { 5 | res.send("Hello World!"); 6 | }); 7 | 8 | app.listen(3000, () => { 9 | console.log("Server listening on port 3000"); 10 | 11 | setInterval(() => { 12 | console.log("Server listening..."); 13 | }, 3000); 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter12/debugging-with-chrome/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debugging-with-chrome", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/debugging-with-chrome/random.js: -------------------------------------------------------------------------------- 1 | module.exports = (n) => { 2 | const randomNumber = Math.floor(Math.random() * n) + "1"; 3 | return randomNumber; 4 | }; 5 | -------------------------------------------------------------------------------- /Chapter12/debugging-with-chrome/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const random = require("./random"); 4 | 5 | app.get("/:number", (req, res) => { 6 | const number = req.params.number; 7 | res.send(random(number).toString()); 8 | }); 9 | 10 | app.listen(3000, () => { 11 | console.log("Server listening on port 3000"); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter12/diagnostic-report/server.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const path = require("path"); 3 | 4 | //process.report.directory = path.join(__dirname, "reports"); 5 | process.report.filename = "my-diagnostic-report.json"; 6 | 7 | http.get("hello://localhost:3000", (response) => {}); -------------------------------------------------------------------------------- /Chapter12/express-debug-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-debug-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/express-debug-app/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | app.get("/", (req, res) => res.send("Hello World!")); 5 | 6 | app.listen(3000, () => { 7 | console.log("Server listening on port 3000"); 8 | }); -------------------------------------------------------------------------------- /Chapter12/express-debug-custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-debug-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/express-debug-custom/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const debug = require("debug")("my-server"); 4 | 5 | app.get("/", (req, res) => { 6 | debug("HTTP GET request to /"); 7 | res.send("Hello World!"); 8 | }); 9 | 10 | app.listen(3000, () => { 11 | console.log("Server listening on port 3000"); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'ejs'); 15 | 16 | app.use(logger('dev')); 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: false })); 19 | app.use(cookieParser()); 20 | app.use(express.static(path.join(__dirname, 'public'))); 21 | 22 | app.use('/', indexRouter); 23 | app.use('/users', usersRouter); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | next(createError(404)); 28 | }); 29 | 30 | // error handler 31 | app.use(function(err, req, res, next) { 32 | // set locals, only providing error in development 33 | res.locals.message = err.message; 34 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 35 | 36 | // render the error page 37 | res.status(err.status || 500); 38 | res.render('error'); 39 | }); 40 | 41 | module.exports = app; 42 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-morgan-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "http-errors": "~1.6.3", 14 | "morgan": "~1.9.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/views/error.ejs: -------------------------------------------------------------------------------- 1 | <h1><%= message %></h1> 2 | <h2><%= error.status %></h2> 3 | <pre><%= error.stack %></pre> 4 | -------------------------------------------------------------------------------- /Chapter12/express-morgan-app/views/index.ejs: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <title><%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter12/express-pino-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-pino-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.17.1", 15 | "express-pino-logger": "^5.0.0", 16 | "pino": "^6.3.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter12/express-pino-app/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const PORT = 3000; 4 | 5 | const pino = require("pino")(); 6 | const logger = require("express-pino-logger")({ 7 | instance: pino, 8 | }); 9 | 10 | app.use(logger); 11 | 12 | app.get("/", (req, res) => { 13 | req.log.info("Generating random number"); 14 | const randomNumber = getRandomNumber(); 15 | res.send(`${randomNumber}`); 16 | }); 17 | 18 | app.listen(PORT, () => pino.info(`Server listening on port ${PORT}`)); 19 | 20 | function getRandomNumber() { 21 | return Math.floor(Math.random() * 100) + 1; 22 | } 23 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | 6 | var winston = require('winston'); 7 | var expressWinston = require('express-winston'); 8 | 9 | var indexRouter = require('./routes/index'); 10 | var usersRouter = require('./routes/users'); 11 | 12 | var app = express(); 13 | 14 | // view engine setup 15 | app.set('views', path.join(__dirname, 'views')); 16 | app.set('view engine', 'ejs'); 17 | 18 | app.use(expressWinston.logger({ 19 | transports: [ 20 | new winston.transports.Console({ 21 | json: true 22 | }) 23 | ] 24 | })); 25 | 26 | app.use(express.json()); 27 | app.use(express.urlencoded({ extended: false })); 28 | app.use(cookieParser()); 29 | app.use(express.static(path.join(__dirname, 'public'))); 30 | 31 | app.use('/', indexRouter); 32 | app.use('/users', usersRouter); 33 | 34 | // catch 404 and forward to error handler 35 | app.use(function(req, res, next) { 36 | next(createError(404)); 37 | }); 38 | 39 | // error handler 40 | app.use(function(err, req, res, next) { 41 | // set locals, only providing error in development 42 | res.locals.message = err.message; 43 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 44 | 45 | // render the error page 46 | res.status(err.status || 500); 47 | res.render('error'); 48 | }); 49 | 50 | module.exports = app; 51 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-winston-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "express-winston": "^4.0.3", 14 | "http-errors": "~1.6.3", 15 | "winston": "^3.2.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /Chapter12/express-winston-app/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter12/stack-trace-app/async-stack-trace.js: -------------------------------------------------------------------------------- 1 | foo().then( 2 | () => console.log("success"), 3 | (error) => console.error(error.stack) 4 | ); 5 | 6 | async function foo() { 7 | await bar(); 8 | } 9 | 10 | async function bar() { 11 | await Promise.resolve(); 12 | throw new Error("Fail"); 13 | } 14 | -------------------------------------------------------------------------------- /Chapter12/stack-trace-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stack-trace-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.17.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/stack-trace-app/routes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = new express.Router(); 3 | 4 | router.get("/", (req, res) => { 5 | res.send(recursiveContent()); 6 | }); 7 | 8 | function recursiveContent(content, i = 15) { 9 | --i; 10 | if (i !== 0) { 11 | return recursiveContent(content, i); 12 | } else { 13 | return content.undefined_property; 14 | } 15 | } 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /Chapter12/stack-trace-app/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const routes = require("./routes"); 3 | const app = express(); 4 | 5 | app.use(routes); 6 | app.listen(3000, () => { 7 | console.log("Server listening on port 3000"); 8 | }); 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------