Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Node.js Development with ES2015 ->

Node.js Development with ES2015 ->

Overview slides of Node.js development. Covers also some syntax of EcmaScript 2015/2016/2017/2018/2019/2020 like await/async with promises. Example code how to build RESTful API connecting to MySQL, Sequelize ORM and MongoDB

D9e65f4b0af059ae9ba243c8c2265e4f?s=128

Jussi Pohjolainen

February 24, 2021
Tweet

Transcript

  1. Node.js Development Jussi Pohjolainen 1

  2. Agenda • Node.js runtime and ES2015/2016/2017 • Architecture, Event Driven

    I/O, Working with npm, Integrating tools with Visual Studio Code, Linting, Node.js modules, Async and Promises • Node.js and building Web Server • Simple web server, express and body-parser modules, RESTful API • Database Connection • Database modules, MySQL/Sequelize ORM/MongoDB 2
  3. Node.js runtime 3

  4. Node.js runtime • Node.js is an open-source, cross-platform JS run-time

    environment • https://nodejs.org/en/ • Executes JS without the browser using V8 • Let's you develop server-side scripting with JavaScript • "JavaScript everywhere" 4
  5. Node.js Internals • Applications/Modules: Your app, Node.js modules, any module

    installed by npm • V8: JavaScript engine by Google and implemented in C++. Same engine can be found from Chrome browser. • Compiles JS to machine code • libuv: C library that provides asynchronous features • Other c/c++ components: c-ares, crypto, http-parser, zlib.. (networking, compressing, encrypting) • Bindings: Glue that binds JavaScript code with C++ - code. Bindings exposes libraries written in C/C++ to JavaScript 5
  6. libuv • libuv is a multi-platform support library with a

    focus on asynchronous I/O • https://github.com/libuv/libuv • Features • Asynchronous TCP and UDP sockets • Asynchronous DNS resolution • Asynchronous file and file system operations • File system events • Node.js provides bindings for the libuv so you can use these with JavaScript 6
  7. Node.js approach: Single-threaded* • All JavaScript you write run in

    one thread, called the main thread • One thread has a event loop that accepts incoming requests • Each new request causes a JavaScript function to fire which can do stuff in non-blocking way • *But Node.js has also a lot of C++ - code.. and C++ has access to threads • JS -> Synchronous C++ // All run in the main thread • JS -> Asynchronous C++ // Can spawn a new thread! 7
  8. Node.js and Threads • So the modules you use in

    Node.js tend to be asynchronous • They do not block the main thread • function(parameters, callback) • When async module has done it's job, it calls the callback function • Control is returned to event loop • The loop decides which function was scheduled for execution 8
  9. Synchronous Example const crypto = require('crypto'); const iteration = 4

    for(let i=0; i<iteration; i++) { // Hashes passwords, takes time const key = crypto.pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512'); } Time 9
  10. Asynchronous Example const crypto = require('crypto'); const iteration = 4

    for(let i=0; i<iteration; i++) { crypto.pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, key) => { console.log(key.toString('hex')); // '3745e48...08d59ae' }); } Invoke callback when job done Time 10
  11. Measuring Time const crypto = require('crypto'); const iteration = 4

    console.time("sync"); for(let i=0; i<iteration; i++) { const key = crypto.pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512'); } console.timeEnd("sync"); MacBook Pro 2018: 376ms 11
  12. Measuring Time const crypto = require('crypto'); const iteration = 4

    console.time("async") for(let i=0; i<iteration; i++) { crypto.pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, key) => {}); } console.timeEnd("async") Hmm….? 12
  13. Measuring Time (without promises) const crypto = require('crypto'); const iteration

    = 4 let howManyCallbacks = 0; console.time("async") for(let i=0; i<iteration; i++) { crypto.pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, key) => { howManyCallbacks++ if(howManyCallbacks === iteration) { console.timeEnd("async") } }); } 108 ms 13
  14. Hello World 14

  15. Node.js hello world 15

  16. Visual Studio Code - tips • Install Code Runner -

    extension for easier running of node (and other) apps • Debugging Node apps is supported by default! • Install ESLint for linting • Install Rest Client for testing 16
  17. npm package manager • Node.js contains a npm that can

    be used to install various extensions and packages • You can install packages • local (specific to your app) • ./node_modules • global (can be used everywhere) • /usr/local/lib/node_modules • When creating a node project, usually you will have a file package.json describing the project and it's dependencies 17
  18. Packages 18

  19. Trying to use package 19 Getting errors here…

  20. 20 Creates package.json with information about our project

  21. npm install project-name-generator 21 Now dir for downloaded packages

  22. cat package.json 22 package.json is updated

  23. Semantic Versioning (semver) • Version numbering: • breaking/major.feature/minor.fix/patch) • for

    example 1.5.4 • Breaking/major • End user code must be updated • Feature/minor • Adding new features • Fix/patch • Backward compatible bug fix 23
  24. Common set of rules: ^ • Common rules • I

    won’t accept any breaking changes. • I will accept new features if they’re not breaking. • I will accept any fixes if they’re not breaking. • To express this • Package version can be ^1.3.5 24
  25. Common set of rules: ~ • Common rules • I

    won’t accept any breaking changes. • I don’t need new features • I will accept any fixes if they’re not breaking. • To express this • Package version can be ~1.3.5 • And to give exact version, just use 1.3.5 25
  26. cat package.json 26 No breaking changes are accepted

  27. cd node_modules 27 The project-name- generator depends on other package

    which is downloaded also!
  28. package-lock.json • The package.json contains direct dependencies • How to

    control the nested dependencies? • The package-lock.json is created for you and contains all the dependencies (dependency tree) • If package.json accept version changes in different time using npm install can result in different dependency versions (might be a problem) • The lock file locks all the versions. • When using npm install it will install exactly the same versions than in the lock file. • To update, use npm update 28
  29. 29

  30. ESLint • ESLint is linting utility (code quality) tool for

    JavaScript • To install • npm install --save-dev eslint • ESLint requires a configuration file • To create one, use • ./node_modules/eslint/bin/eslint.js --init • You can answer to questions about style guide or take popular style guides 30
  31. Answering to Style Guide Questions 31

  32. Running ESLint Add no- console: off to init file 32

  33. module.exports = { "env": { "es6": true, "node": true },

    "extends": "eslint:recommended", "parserOptions": { "sourceType": "module" }, "rules": { "no-console": "off", "indent": [ "error", 4 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "double" ], "semi": [ "error", "always" ] } }; Add no- console: off to init file Lot of rules https://eslint.org/docs/rules/ 33
  34. VS Code has also ESLint plugin for integrating linting in

    code editor 34
  35. Different Style Guides • AirBnb • Google • "Standard" Style

    • Crockford's Coding Standards for JavaScript • NodeJS Style Guide 35
  36. Holy War about Styles: Indentation https://hackernoon.com/what-javascript-code-style-is-the-most-popular-5a3f5bec1f6f 36

  37. Holy War about Styles: Lint tool? https://hackernoon.com/what-javascript-code-style-is-the-most-popular-5a3f5bec1f6f 37

  38. ESLint: Choosing Style Guide 38

  39. Standard Rules • 2 spaces for indentation • Single quotes

    for string • No unused variables • No semicolons • ... • https://standardjs.com/ 39
  40. Promises 40

  41. Async callback hell? • When we tend to have async

    call that has another async call (that has a async call..) it can be tedious to implement • Promises (ES2015) can help this a bit • When Node.js was implement Promises were not part of the ECMAScript so there are different approaches to this • In Util.promisify you can promisify a any callback function with result and error • So wrapping the "old" APIs with the Util.promisify can help a bit 41
  42. Using Promises function promiseFunction(resolve, reject) { // time consuming async

    stuff if(true) { resolve("OK!"); } else { reject("Failed!"); } } function onSuccess(msg) { console.log(msg); } function onError(msg) { console.log(msg); } let promise = new Promise(promiseFunction); promise.then(onSuccess).catch(onError); 42
  43. const crypto = require('crypto'); function promiseFunction(resolve, reject) { crypto.pbkdf2('secret', 'salt',

    100000, 64, 'sha512', (err, key) => { if(err) { reject(err) } else { resolve(key.toString('hex')) } }); } function onSuccess(key) { console.log(key); } function onError(msg) { console.log(msg); } let promise = new Promise(promiseFunction); promise.then(onSuccess).catch(onError); 43 Wrapping async function inside of promise
  44. Example const crypto = require('crypto'); function pdkdf2(password) { function promiseFunction(resolve,

    reject) { crypto.pbkdf2(password, 'salt', 100000, 64, 'sha512', (err, key) => { if(err) { reject(err) } else { resolve(key.toString('hex')) } }); } return new Promise(promiseFunction) } pdkdf2('mypassword').then((key) => console.log(key)).catch((err) => console.log(err)); 44 Function returning a promise will create a cleaner solution
  45. Node.JS callbacks const fs = require('fs'); fs.readFile('mytest.json', function (error, text)

    { if (error) { console.error('Error while reading config file'); } else { try { const obj = JSON.parse(text); console.log(JSON.stringify(obj)); } catch (e) { console.error('Invalid JSON in file'); } } } ); 45 Async function for reading a file Notice two different approaches for error handling
  46. Node.JS promise const util = require('util'); const fs = require('fs');

    const promiseReadFile = util.promisify(fs.readFile); promiseReadFile('mytest.json') .then(function (text) { // const obj = JSON.parse(text); console.log(JSON.stringify(obj)); }) .catch(function (error) { // // File read error or JSON SyntaxError console.error('An error occurred', error); }); 46 util.promisify is Node 8 feature Now we can do the same with promises. Notice that one catch for every exception or error
  47. ES2017: async / await • When chaining multiple promises the

    code can get also complicated • doSomething().then((result) => something(result)).then(…).then(…)… • async and await can help 47
  48. What Happens Here? const fetch = require('node-fetch') fetch('https://swapi.co/api/people/1/') .then((httpResp1) =>

    httpResp1.json()) .then((characterJson) => fetch(characterJson.films[0])) .then((httpResp2) => httpResp2.json()) .then((filmJson) => console.log(filmJson)) 48
  49. Async function function f1() { // Returns a resolved Promise

    object with the given value return Promise.resolve(1); } f1().then((integer) => console.log(integer)); // 1 async function f2() { return 1 } f2().then((integer) => console.log(integer)) 49
  50. Async function function f1() { // Returns a resolved Promise

    object with the given value return Promise.resolve(1); } f1().then((integer) => console.log(integer)); // 1 async function f2() { return 1 } f2().then((integer) => console.log(integer)) 50 Will return a promise!
  51. Promise (recap) function delay(secs, text) { function promiseFunction(resolve, reject) {

    setTimeout(() => resolve(text), secs) } return new Promise(promiseFunction) } delay(1000, 'hello').then((text) => console.log(text)) Will output ’hello’ after 1000 sec 51
  52. With async there is await function delay(secs, text) { function

    promiseFunction(resolve, reject) { setTimeout(() => resolve(text), secs) } return new Promise(promiseFunction) } async function doIt() { let text = await delay(1000, 'hello') console.log(text) } console.log('call async function') doIt() console.log('async function has been called') Waits until the result. Execution still continues in other parts of the app (async) 52 Output: call async function async function has been called hello
  53. Using async function delay(secs, text) { function promiseFunction(resolve, reject) {

    setTimeout(() => resolve(text), secs) } return new Promise(promiseFunction) } async function doIt() { let text1 = await delay(1000, 'hello') let text2 = await delay(1000, text1 + ' world') return text2 // Promise.resolve(text2) } doIt().then((result) => console.log(result)) Returns a promise! 53 Order here is guaranteed! Two delay promises are run in particular order. Both are run in async
  54. Without async function delay(secs, text) { function promiseFunction(resolve, reject) {

    setTimeout(() => resolve(text), secs) } return new Promise(promiseFunction) } delay(1000, 'hello’) .then((text1) => delay(1000, text1 + ' world’)) .then(text2 => console.log(text2)) Can be a bit hard to read.. 54
  55. Without async const fetch = require('node-fetch') fetch('https://swapi.co/api/people/1/') .then((httpResp1) => httpResp1.json())

    .then((characterJson) => fetch(characterJson.films[0])) .then((httpResp2) => httpResp2.json()) .then((filmJson) => console.log(filmJson)) 55 Can be a bit hard to read..
  56. With async and await const fetch = require('node-fetch') async function

    fetchFilm(url) { const httpResp1 = await fetch(url) const characterJson = await httpResp1.json() const httpResp2 = await fetch(characterJson.films[0]) const filmJson = await httpResp2.json() return filmJson } fetchFilm('https://swapi.co/api/people/1/’) .then((filmJson) => console.log(filmJson)).catch(err => console.log(err)) 56 Easier to understand?
  57. EcmaScript 2015 (6th edition) 57

  58. Year Edition Naming 1997 1 .. 1998 2 .. 1999

    3 Regex, better string handling, try/catch,... Abandoned 4 .. 2009 5 strict mode, getters and setters, JSON.. 2015 6 ES2015 classes, modules, arrow functions, collections ... 2016 7 ES2016 Math.pow => **, array.prototype.includes 2017 8 ES2017 await/async 2018 9 ES2018 spread operator, rest parameters, async iteration 2019 10 ES2019 Array.flat, Array.flatMap 2020 11 ES2020 BigInt, nullish coalescing operator, optional chaining, globalThis ESNext await and async is rather new 58
  59. Some new ES2015 New Features • Arrow Functions • Parameter

    values • Object properties • Classes • Modules 59
  60. Lexical this var object = { array: [0,1,2,3,4], doIt: function()

    { this.array.forEach(function(value) { this.array.push(value); }); } } object.doIt(); Cannot read property 'push' of undefined 60
  61. Lexical this var object = { array: [0,1,2,3,4], doIt: function()

    { var _this = this; this.array.forEach(function(value) { _this.array.push(value); }); } } object.doIt(); It works when using closure 61
  62. Lexical this var object = { array: [0,1,2,3,4], doIt: function()

    { this.array.forEach((value) => { this.array.push(value); }); } } object.doIt(); Works! 62
  63. Variadic Parameter Values function printIt(amount, ...text) { for(let i =

    0; i < amount; i++) { for(let j = 0; j < text.length; j++) { console.log(text[i]); } } } printIt(1, "text"); printIt(2, "hello", "world"); 63
  64. Interpolation of String var message = "hello"; var html =

    ` <div> <p>${message}</p> </div> `; console.log(html); 64
  65. Object Properties let x = 0; let y = 1;

    let obj = { x, y }; console.log(obj.x); console.log(obj.y); 65
  66. class Shape { constructor(x, y, color) { this.x = x;

    this.y = y; this.color = color; } } class Rectangle extends Shape { constructor (x, y, color, width, height) { super(x, y, color); this.width = width; this.height = height; } } class Circle extends Shape { constructor (x, y, color, radius) { super(x, y, color); this.radius = radius; } } let circle = new Circle(0,0,"red",5); console.log(circle); A lot nicer syntax for creating inheritance! 66
  67. class Shape { constructor(x, y, color) { this.x = x;

    this.y = y; this.color = color; } } class Rectangle extends Shape { constructor (x, y, color, width, height) { super(x, y, color); this.width = width; this.height = height; } } class Circle extends Shape { constructor (x, y, color, radius) { super(x, y, color); this.radius = radius; } } Shape.prototype.hello = "world"; let circle = new Circle(0,0,"red",5); console.log(circle.hello); But it is syntactical sugar!! 67
  68. Modules 68

  69. About Modules • Modules Systems 1. AMD Specification (RequireJS) 2.

    CommonJS Modules (NodeJS) 3. ES2015 official module (React) • In EcmaScript 2015 for the first time it's built into language. • In node 8.5.0 you can use these as ”experimental”. In Node 10.0 LTS the flag should be removed • node --experimental-modules index.js • It's possible to compile ES6 Modules to AMD or CommonJS 69
  70. EcmaScript 2015: app.js import { generateRandom, sum } from './utility.js';

    console.log(generateRandom()); // logs a random number console.log(sum(1, 2)); // 3 70
  71. EcmaScript 2015: utility.js function generateRandom() { return Math.random(); } function

    sum(a, b) { return a + b; } export { generateRandom, sum } 71
  72. Modify package.json { "name": "projekti", "version": "1.0.0", "description": "", "main":

    "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "type": "module" } 72
  73. Run • node --experimental-modules index.js 73

  74. CommonJS • CommonJS is a project with the goal of

    specifying ecosystem for JS outside of Browser • Was started by Mozilla engineer, inital name ServerJS • CommonJS described a lot of specifications, including modules • This module specification is implemented in NodeJS • require to include modules • exports to make things available 74
  75. Example Module: randomModule.js 'use strict'; /** * Returns random integer

    number between [min, max]. * @param {number} min * @param {number} max * @return {number} */ module.exports = function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }; Uses JSdoc 75 Exports the function
  76. Example Module 'use strict'; var random = require("./randomModule"); console.log(random(1,10)); 76

    Importing the function
  77. Exporting Object 'use strict'; var MyMath = { random: function(min,

    max) { return Math.floor(Math.random() * (max - min + 1)) + min; }, abs: function(value) { return (value < 0) ? value * -1: value; } }; module.exports = MyMath; 77 Exporting a whole object
  78. Importing Object 'use strict'; var MyMath = require("./mathModule"); console.log(MyMath.random(1,10)); console.log(MyMath.abs(-7));

    78 And requiring the object
  79. ES9: Spread Operator function sum(x, y, z) { return x

    + y + z; } const numbers = [1, 2, 3]; console.log(sum(...numbers)); 79
  80. ES9: Spread Operator let parts = ['shoulders', 'knees']; let lyrics

    = ['head', ...parts, 'and', 'toes']; // ["head", "shoulders", "knees", "and", "toes"] 80
  81. ES9: Spread Operator let obj1 = { foo: 'bar', x:

    42 }; let obj2 = { foo: 'baz', y: 13 }; let clonedObj = { ...obj1 }; // Object { foo: "bar", x: 42 } let mergedObj = { ...obj1, ...obj2 }; // Object { foo: "baz", x: 42, y: 13 } 81
  82. ES11: Nullish coalescing operator undefined ?? "string" // -> "string"

    null ?? "string" // "string" false ?? "string" // -> false NaN ?? "string" // -> NaN 82
  83. Optional Chaining const zipcode = person?.address?.zipcode 83

  84. flat const arr1 = [0, 1, 2, [3, 4]]; console.log(arr1.flat());

    // expected output: [0, 1, 2, 3, 4] const arr2 = [0, 1, 2, [[[3, 4]]]]; console.log(arr2.flat(2)); // expected output: [0, 1, 2, [3, 4]] 84
  85. Keyword this in node 85

  86. Output of this? const doIt1 = function() { console.log(this) }

    const doIt2 = () => { console.log(this) } doIt1() doIt2() console.log(this) 86
  87. Remember lexical this… const doIt1 = function() { console.log(this) }

    const doIt2 = () => { console.log(this) } var _this = this const doIt2 = function () { console.log(_this) } doIt1() doIt2() console.log(this) 87 The arrow syntax uses this from “global” scope “global” scope here has different meaning for this than inside of a function..
  88. Using this in functions function doIt(name) { this.name = name

    } let obj = {} doIt.apply(obj, ["jack"]) console.log(obj) 88 this now refers to obj Calling the function and defining the meaning of this
  89. Keyword this in Node.js: Wrapper Function var context = (function

    (exports, require, module, __filename, __dirname) { // Your code }); var module = {exports:{}} context.apply(module.exports, [module.exports, require, module, "FILE_NAME", "DIR_NAME"]); 89 In Node, your whole code is inside of a wrapper function! Calling the wrapper function and defining the meaning of this
  90. Printing out this module.exports.stuff = "hello" console.log(this) { stuff: 'hello'

    } 90
  91. Printing out this function doIt() { console.log(this) } doIt() Global

    object 91
  92. What happens here? stuff = 'hello' function doIt() { console.log(this.stuff)

    } doIt() 92
  93. What happens here? index.js require('./utility.js') console.log(stuff) utility.js stuff = 'hello'

    93
  94. Preventing Access to Global Object 'use strict' function doIt() {

    console.log(this) } doIt() undefined 94
  95. Lab 95

  96. Node.js Web Server 96

  97. Http Server: index.js const http = require('http') // Create a

    server (http.Server) and provide a callback function const server = http.createServer((request, response) => { response.end('Path: ' + request.url) }) // Start the server in port 8080 server.listen(8080, () => console.log(`Server listening in port ${server.address().port}`)) http.IncomingMessage http.ServerResponse 97
  98. Express.js • Express.js is a Node.js web application server framework

    • Gives you a layer of fundamental web application features • To install: • npm install express 98
  99. Web Server 'use strict' const express = require('express') // App

    object has methods for routing http requests, configuring // middleware, rendering html pages, registering template engine const app = express() // Listen requests for root (/) app.get('/', (req, res) => { res.send('Hello World!') }) const server = app.listen(8080, () => { console.log(`Listening on port ${server.address().port}`) }) 99
  100. Tip: nodemon • To restart server automatically after changing code,

    use nodemon instead of node • npm install -g nodemon • And • nodemon myapp.js • Workflow without nodemon • https://twitter.com/unicodeveloper/status/1037712553873424385 100
  101. Lab 101

  102. Middleware • Middleware is a function that receives request and

    response - objects • Can • Execute code • Make changes to request and response objects • End the req/res cycle • Call the nest middleware function • Different types of middleware • App level • Router level • Error handling • ... 102
  103. Application level app.use((req, res, next) => { console.log(new Date(), req.method,

    req.url) next() }) app.use('/someurl', (req, res, next) => { console.log(new Date(), req.method, req.url) next() }) Called when url contains /someurl 103
  104. Router middleware • Router is kind of "mini-application" • Router

    behaves like middleware, so you can use it with app.use() • To create router, use • let router = express.Router() • Then you can add http method routes to the object • router.get('/router1', (req, res) => { res.send('router 1') }) • And add it as a middleware • app.use('/root', router) 104
  105. 'use strict' const express = require('express') const app = express()

    const listener = app.listen(8080, () => { console.log(`Listening on port ${listener.address().port}`) }) // Creating router - object let calendar = express.Router() let users = express.Router() // Use it as middleware app.use('/calendar', calendar) app.use('/users', users) calendar.get('/2018-09-09', (req, res) => { res.send('calendar information') }) users.get('/1', (req, res) => { res.send('user information') }) 105 Called when url contains /calendar/2018-09-09
  106. Lab 106

  107. Separating routing to different file app.js 'use strict' const express

    = require('express') const app = express() const users = require('./routes/index.js') // Use it as middleware app.use('/users', users) const listener = app.listen(8080, () => { console.log(`Listening on port ${listener.address().port}`) }) routes/index.js 'use strict' const express = require('express') let users = express.Router() users.get('/1', (req, res) => { res.send('user information') }) module.exports = users 107
  108. Lab 108

  109. Routing 'use strict' const express = require('express') let users =

    express.Router() users.get('/1', (req, res) => { res.send('Getting user') }) users.post('/', (req, res) => { res.send('Adding user') }) users.delete('/1', (req, res) => { res.send('Deleting user') }) module.exports = users 109
  110. Routing, using Regex 'use strict' const express = require('express') let

    users = express.Router() const number = new RegExp('^/[0-9]+$') users.get(number, (req, res) => { res.send('Getting user') }) users.get(/^\/test$/, (req, res) => { res.send('Test user') }) module.exports = users /99 /test 110
  111. Path to Regexp • Path-to-regexp is a module that turns

    string into regular expression • https://www.npmjs.com/package/path-to-regexp • It will make easier to create regexp objects, especially if they have parameters • For example, a string containing '/[1-9]+' is transformed into a regex object /^\/[1-9]+$/ 111
  112. Routing, using path to regexp 'use strict' const express =

    require('express') let users = express.Router() users.get('/[1-9]+', (req, res) => { res.send('Getting user') }) users.get('/test', (req, res) => { res.send('Test user') }) module.exports = users 112 string => regexp
  113. Using parameters 'use strict' const express = require('express') let users

    = express.Router() users.get('/:myVariable([1-9]+)', (req, res) => { res.send(`Getting user ${req.params.myVariable}`) }) module.exports = users 113 Create a new variable containing the stuff in url Use req – object to access the variable
  114. Lab 114

  115. Creating "Data object" class User { constructor (id, name) {

    this.id = id this.name = name } } module.exports = User Just a plain class for creating users 115
  116. 'use strict' const User = require('../dataobjects/user.js') const express = require('express')

    let users = express.Router() const database = new Set([new User(1, 'jack smith'), new User(2, 'tina jackson')]) users.get('/:urlId([1-9]+)', (req, res) => { let userObject = {} const urlId = Number(req.params.urlId) database.forEach((user) => { if (user.id === urlId) { userObject = user } }) res.send(JSON.stringify(userObject)) }) module.exports = users Creating "database" Importing the class If user with given id is found Sends a string response 116
  117. Headers curl -i http://localhost:8080/users/1 HTTP/1.1 200 OK X-Powered-By: Express Content-Type:

    text/html; charset=utf-8 Content-Length: 28 ETag: W/"1c-VJ4vRdnoG3woR9lnRtdA+3Qm+l4" Date: Thu, 16 Aug 2018 09:35:57 GMT Connection: keep-alive {"id":1,"name":"jack smith"} Well this is not right... 117
  118. Response methods Sets also the content- type: res.json("{}") But this

    can detect automatically if given json res.send({}) 118
  119. 'use strict' const User = require('../dataobjects/user.js') const express = require('express')

    let users = express.Router() const database = new Set([new User(1, 'jack smith'), new User(2, 'tina jackson')]) users.get('/:urlId([1-9]+)', (req, res) => { let userObject = {} const urlId = Number(req.params.urlId) database.forEach((user) => { if (user.id === urlId) { userObject = user } }) res.send(userObject) }) module.exports = users No need for transform the json to string 119
  120. HTTP POST BODY Parsers • In most recent version of

    Express, http post body parser is prebuilt and to be used as middleware app.use(express.json()) • This will take the HTTP POST Body and transform it to json and can be read from request object users.post('/', (req, res) => { let user = req.body database.add(user) res.send(user) }) 120
  121. Node.js Connecting to DB 121

  122. Using Databases • Adding capability to connect to database is

    just a matter of installing Node.js db driver • For each database you will have different driver • MySQL • MongoDB • PostgreSQL ... • Installing the driver, for example mysql • npm install mysql 122
  123. MariaDB [test]> create table users (id int NOT NULL AUTO_INCREMENT,

    name varchar(255) NOT NULL, PRIMARY KEY (id)); Query OK, 0 rows affected (0.01 sec) MariaDB [test]> insert into users (name) values ('jack smith'); Query OK, 1 row affected (0.01 sec) MariaDB [test]> insert into users (name) values ('tina jackson'); Query OK, 1 row affected (0.00 sec) MariaDB [test]> select * from users; +----+--------------+ | id | name | +----+--------------+ | 1 | jack smith | | 2 | tina jackson | +----+--------------+ 2 rows in set (0.01 sec) 123
  124. npm install mysql const mysql = require('mysql') const connection =

    mysql.createConnection({ host: 'localhost', user: '', password: '', database: 'test' }) connection.connect() connection.query('select * from users', (err, users) => { if (err) { throw err } users.forEach((user) => console.log(`${user.id} ${user.name}`)) }) // will wait if previously enqueued queries connection.end() 124
  125. Insert const mysql = require('mysql') const connection = mysql.createConnection({ host:

    'localhost', user: '', password: '', database: 'test' }) connection.connect() connection.query('insert into users (name) values ("tina jackson")', (err, result) => { if (err) { throw err } console.log(result.affectedRows) }) // will wait if previously enqueued queries connection.end() 125
  126. Example of Express + MySQL 126

  127. const User = require('../dataobjects/user.js') const express = require('express’) let users

    = express.Router() const crudRepository = require('../database/crudrepository.js') users.get('/', (req, res) => { crudRepository.findAll((result) => res.send(result)) }) users.post('/', (req, res) => { let user = req.body crudRepository.save(user, (result) => res.send(result)) }) module.exports = users 127 The router here is unaware of the database. CrudRepository contains basic function for finding and saving
  128. config.js const config = { host: 'localhost', user: '', password:

    '', database: 'test' } module.exports = config 128
  129. const mysql = require('mysql') const config = require('./config.js') class CrudRepository

    { constructor () { this.connection = mysql.createConnection(config) this.connection.connect() // If ctrl-c, end connection process.on('SIGINT', () => this.connection.end(() => process.exit())); } save (user, callback) { const sql = `insert into users (name) values ('${user.name}')` this.connection.query(sql, (err, results) => { callback({"id": results.insertId}) }) } findAll (callback) { this.connection.query('select * from users', (err, users) => { callback(users) }) } } module.exports = new CrudRepository() 129
  130. Sequalize ORM 130

  131. Sequelize • Sequelize is a promise-based Node.js ORM for •

    Postgres • MySQL, MariaDB • SQLite • Microsoft SQL Server 131
  132. Getting Started • Install sequelize • npm install sequelize •

    And your database • npm install mysql2 | mariadb | sqlite3 | tedious 132
  133. Connection: MariaDB const sequelize = new Sequelize("localhost", "user", "pw", {

    host: "localhost", dialect: "mariadb", define: { timestamps: false, }, }); 133 By default, Sequelize automatically adds the fields createdAt and updatedAt to your table. You can disable this feature.
  134. Connection: SQLite const sequelize = new Sequelize({ dialect: "sqlite", storage:

    "path/to/db.sqlite" define: { timestamps: false, }, }); 134
  135. Connection: SQLite in memory const sequelize = n)ew Sequelize('sqlite::memory:') 135

  136. Testing the Connection // Location will be the table name

    to be Locations, // even person -> people will work! const Location = sequelize.define("Location", { latitude: { type: DataTypes.FLOAT }, longitude: { type: DataTypes.FLOAT }, }); const connect = async () => { await sequelize.authenticate(); // creates the table if it doesn't exist await Location.sync(); console.log("database connection now open"); }; connect(); 136
  137. Usage let all = await Location.findAll() let one = await

    Location.findAll({ where: { id : 1 }}) let result = await Location.destroy({ where: { id }}) let result = await Location.create({ .. }) 137
  138. MongoDB • MongoDB is document-oriented database, like noSQL • Uses

    JSON – like documents • Collections are like ”tables” and contains documents • Documents are JSON data • There are no fixed schema 138
  139. Install and Access To start mongodb To access mongodb 139

  140. Default db location By default /data/db is the location for

    the db files. Must have proper r/w access 140
  141. To start mongo • You can define mongodb path •

    mongod --dbpath /new/path/ • And after this • mongo 141
  142. Free mongodb on clound • Create account • cloud.mongodb.com •

    Create new database and collection 142
  143. Usage from cli • Show databases • show databases •

    Create database • use locationsdb • Insert into locationsdb a new collection with a document • db.locationscollection.insert({longitude: 60, latitude: 60}) • Select * • db.locationscollection.find() • Select the document with longitude = 60 • db.locationscollection.find({longitude: 60}) • To see which db is active • db 143
  144. 144

  145. 145

  146. 146

  147. 147

  148. 148

  149. Node.js mongodb driver • Install • npm install mongodb •

    In code require MongoClient • const MongoClient = require('mongodb').MongoClient; • And connect • const url = 'mongodb://localhost:27017'; • MongoClient.connect(url, {useNewUrlParser: true}, (err, client) => { .. } 149
  150. Example const MongoClient = require('mongodb').MongoClient; const url = 'mongodb://localhost:27017’; MongoClient.connect(url,

    {useNewUrlParser: true}, (err, client) => { if(err) { throw err } console.log("Connected successfully to server") const database = client.db('locationsdb’) client.close() }); 150
  151. const MongoClient = require('mongodb').MongoClient; const url = 'mongodb://localhost:27017'; MongoClient.connect(url, {useNewUrlParser:

    true}, (err, client) => { if(err) { throw err } console.log("Connected successfully to server") const database = client.db('locationsdb') const loc = {latitude: 50, longitude: 60} insertDocument(loc, database, (result) => { console.log(result.insertedCount) client.close() }) }); const insertDocument = function(obj, db, callback) { const collection = db.collection('locationscollection'); collection.insertOne(obj, (err, result) => { if(err) { throw err } console.log("Inserted document into the collection"); callback(result); }); } Function that inserts a doc 151
  152. Finding const findAll = function(db, callback) { const collection =

    db.collection('locationscollection'); collection.find({}).toArray(function(err, docs) { if(err) { throw err } callback(docs); }); } 152
  153. const MongoClient = require('mongodb').MongoClient; const url = 'mongodb://localhost:27017'; MongoClient.connect(url, {useNewUrlParser:

    true}, (err, client) => { if(err) { throw err } console.log("Connected successfully to server") const database = client.db('locationsdb') const loc = {latitude: 50, longitude: 60} insertDocument(loc, database, (result) => { console.log(result.insertedCount) findAll(database, (result) => { console.log(result) client.close() }) }) }); 153