Slide 1

Slide 1 text

Node.js Development Jussi Pohjolainen 1

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Node.js runtime 3

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Synchronous Example const crypto = require('crypto'); const iteration = 4 for(let i=0; i

Slide 10

Slide 10 text

Asynchronous Example const crypto = require('crypto'); const iteration = 4 for(let i=0; i { console.log(key.toString('hex')); // '3745e48...08d59ae' }); } Invoke callback when job done Time 10

Slide 11

Slide 11 text

Measuring Time const crypto = require('crypto'); const iteration = 4 console.time("sync"); for(let i=0; i

Slide 12

Slide 12 text

Measuring Time const crypto = require('crypto'); const iteration = 4 console.time("async") for(let i=0; i {}); } console.timeEnd("async") Hmm….? 12

Slide 13

Slide 13 text

Measuring Time (without promises) const crypto = require('crypto'); const iteration = 4 let howManyCallbacks = 0; console.time("async") for(let i=0; i { howManyCallbacks++ if(howManyCallbacks === iteration) { console.timeEnd("async") } }); } 108 ms 13

Slide 14

Slide 14 text

Hello World 14

Slide 15

Slide 15 text

Node.js hello world 15

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Packages 18

Slide 19

Slide 19 text

Trying to use package 19 Getting errors here…

Slide 20

Slide 20 text

20 Creates package.json with information about our project

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

cat package.json 22 package.json is updated

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

cat package.json 26 No breaking changes are accepted

Slide 27

Slide 27 text

cd node_modules 27 The project-name- generator depends on other package which is downloaded also!

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

29

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Answering to Style Guide Questions 31

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

VS Code has also ESLint plugin for integrating linting in code editor 34

Slide 35

Slide 35 text

Different Style Guides • AirBnb • Google • "Standard" Style • Crockford's Coding Standards for JavaScript • NodeJS Style Guide 35

Slide 36

Slide 36 text

Holy War about Styles: Indentation https://hackernoon.com/what-javascript-code-style-is-the-most-popular-5a3f5bec1f6f 36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

ESLint: Choosing Style Guide 38

Slide 39

Slide 39 text

Standard Rules • 2 spaces for indentation • Single quotes for string • No unused variables • No semicolons • ... • https://standardjs.com/ 39

Slide 40

Slide 40 text

Promises 40

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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!

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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..

Slide 56

Slide 56 text

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?

Slide 57

Slide 57 text

EcmaScript 2015 (6th edition) 57

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Some new ES2015 New Features • Arrow Functions • Parameter values • Object properties • Classes • Modules 59

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Lexical this var object = { array: [0,1,2,3,4], doIt: function() { this.array.forEach((value) => { this.array.push(value); }); } } object.doIt(); Works! 62

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

Interpolation of String var message = "hello"; var html = `

${message}

`; console.log(html); 64

Slide 65

Slide 65 text

Object Properties let x = 0; let y = 1; let obj = { x, y }; console.log(obj.x); console.log(obj.y); 65

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Modules 68

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

EcmaScript 2015: app.js import { generateRandom, sum } from './utility.js'; console.log(generateRandom()); // logs a random number console.log(sum(1, 2)); // 3 70

Slide 71

Slide 71 text

EcmaScript 2015: utility.js function generateRandom() { return Math.random(); } function sum(a, b) { return a + b; } export { generateRandom, sum } 71

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Run • node --experimental-modules index.js 73

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

Example Module 'use strict'; var random = require("./randomModule"); console.log(random(1,10)); 76 Importing the function

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

Importing Object 'use strict'; var MyMath = require("./mathModule"); console.log(MyMath.random(1,10)); console.log(MyMath.abs(-7)); 78 And requiring the object

Slide 79

Slide 79 text

ES9: Spread Operator function sum(x, y, z) { return x + y + z; } const numbers = [1, 2, 3]; console.log(sum(...numbers)); 79

Slide 80

Slide 80 text

ES9: Spread Operator let parts = ['shoulders', 'knees']; let lyrics = ['head', ...parts, 'and', 'toes']; // ["head", "shoulders", "knees", "and", "toes"] 80

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

ES11: Nullish coalescing operator undefined ?? "string" // -> "string" null ?? "string" // "string" false ?? "string" // -> false NaN ?? "string" // -> NaN 82

Slide 83

Slide 83 text

Optional Chaining const zipcode = person?.address?.zipcode 83

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

Keyword this in node 85

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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..

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

Printing out this module.exports.stuff = "hello" console.log(this) { stuff: 'hello' } 90

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

Preventing Access to Global Object 'use strict' function doIt() { console.log(this) } doIt() undefined 94

Slide 95

Slide 95 text

Lab 95

Slide 96

Slide 96 text

Node.js Web Server 96

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

Lab 101

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

'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

Slide 106

Slide 106 text

Lab 106

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Lab 108

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

Lab 114

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

'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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

Response methods Sets also the content- type: res.json("{}") But this can detect automatically if given json res.send({}) 118

Slide 119

Slide 119 text

'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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

Node.js Connecting to DB 121

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

Example of Express + MySQL 126

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

config.js const config = { host: 'localhost', user: '', password: '', database: 'test' } module.exports = config 128

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

Sequalize ORM 130

Slide 131

Slide 131 text

Sequelize • Sequelize is a promise-based Node.js ORM for • Postgres • MySQL, MariaDB • SQLite • Microsoft SQL Server 131

Slide 132

Slide 132 text

Getting Started • Install sequelize • npm install sequelize • And your database • npm install mysql2 | mariadb | sqlite3 | tedious 132

Slide 133

Slide 133 text

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.

Slide 134

Slide 134 text

Connection: SQLite const sequelize = new Sequelize({ dialect: "sqlite", storage: "path/to/db.sqlite" define: { timestamps: false, }, }); 134

Slide 135

Slide 135 text

Connection: SQLite in memory const sequelize = n)ew Sequelize('sqlite::memory:') 135

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

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

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

Install and Access To start mongodb To access mongodb 139

Slide 140

Slide 140 text

Default db location By default /data/db is the location for the db files. Must have proper r/w access 140

Slide 141

Slide 141 text

To start mongo • You can define mongodb path • mongod --dbpath /new/path/ • And after this • mongo 141

Slide 142

Slide 142 text

Free mongodb on clound • Create account • cloud.mongodb.com • Create new database and collection 142

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

144

Slide 145

Slide 145 text

145

Slide 146

Slide 146 text

146

Slide 147

Slide 147 text

147

Slide 148

Slide 148 text

148

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

Finding const findAll = function(db, callback) { const collection = db.collection('locationscollection'); collection.find({}).toArray(function(err, docs) { if(err) { throw err } callback(docs); }); } 152

Slide 153

Slide 153 text

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