javascript on the serverbouvin/itwot/2019/slides/javascript on the server.pdf · ./index.js it is...
TRANSCRIPT
![Page 1: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/1.jpg)
JavaScript on the ServerNiels Olof Bouvin
1
![Page 2: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/2.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
2
![Page 3: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/3.jpg)
Client/server communication
The Web is an example of a client/server architecture clients make requests servers return resources communication is always initiated by the client
In the context of the Web, clients are (usually) Web browsers, though they can be anything that speaks http and https
http and https are the protocols with which Web clients and servers communicate
Server
Client Clientrequest
resource requestresource
3
![Page 4: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/4.jpg)
The Uniform Resource Locator
Combines protocol the method with which computers communicate, e.g., http, https, ftp, rtp, …
with host or server name usually resolved using DNS
resource name could just be a file name, but could also something more general (we’ll return to this)
and fragment identifier used to point into a resource—usually a text-based one
protocolz }| {https ://
serverz }| {users�cs.au.dk/
resourcez }| {bouvin/dBIoTP2PC/2017/exam.html#
fragmentz }| {topics
4
![Page 5: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/5.jpg)
The HyperText Transfer ProtocolAt its simplest, a request and response for a resource
but in reality, it is a bit more complex and far richer: http://127.0.0.1:8080/hello.txt
GET /hello.txt HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: curl/7.54.0 Accept: */*
HTTP/1.1 200 OK server: ecstatic-2.2.1 last-modified: Tue, 02 Jan 2018 11:54:58 GMT etag: "77865272-13-"2018-01-02T11:54:58.000Z"" cache-control: max-age=3600 content-length: 13 content-type: text/plain; charset=UTF-8 Date: Tue, 02 Jan 2018 12:01:52 GMT Connection: keep-alive
Hello World!
request: (from client)
response: (from server)
headers explaining what the client wants and what the server delivers
the actual resource(A blank line)
5
![Page 6: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/6.jpg)
Operating on resources
HTTP supports four main methods:
GETretrieve the state of a resource — don’t modify it
POSTcreate new resource, do not specify identifier
PUTupdate existing resource, or create new resource with an identifier
DELETEremove a resource
6
![Page 7: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/7.jpg)
A selection of HTTP status codes200 OK
Standard response for successful HTTP requests.
201 Created The request has been fulfilled and resulted in a new resource being created
202 Accepted The request has been accepted, but is not yet fulfilled
301 Moved Permanently This and all future requests should be directed to the given URI
404 Not Found The requested resource could not be found
500 Internal Server Error The server has experienced an error, and could not fulfil the request
7
![Page 8: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/8.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
8
![Page 9: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/9.jpg)
What should a Web server do?
Receive a Request extract information from the Request’s header and body, such as resource identification (‘/min/test.txt’), type of operation (GET, PUT, …), acceptable data formats (‘text/html’), etc.
Create a Response return a suitable resource for the Request of the appropriate data type and with the correct information in the header
This is all
9
![Page 10: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/10.jpg)
Back to Hello World
Now that we are starting server programming, we should begin using proper tools and structure in our code
that means using NPM to handle the basics, such as starting the program, and keeping track of necessary modules, which is done through the package.json file as well as having a systematic approach to folder structure
A new project can be initialised with npm init -fy
10
![Page 11: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/11.jpg)
./package.json
I have filled out a few fields, including start and author
. ├── app │ └── index.js ├── index.js └── package.json
{ "name": "basic-hello-world", "version": "1.0.0", "main": "basic-hello-world.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "Niels Olof Bouvin", "license": "ISC", "description": "A simple hello world server" }
11
![Page 12: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/12.jpg)
./index.js
It is considered good coding style to let the central index.js be sparse, and let the actual functionality reside in appropriately named folders and files
. ├── app │ └── index.js ├── index.js └── package.json
'use strict' require('./app/index')
12
![Page 13: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/13.jpg)
./app/index.js
http.createServer() requires a callback function that gets a request and a response object
the request object is read (quite simply, here) and the response object has data added to it, and is finalised with .end(), which returns the response to the Web browser but first, the server must be set to listen to incoming requests on a specific port
. ├── app │ └── index.js ├── index.js └── package.json
'use strict' const http = require('http')
const port = 3000 let counter = 0
const server = http.createServer((request, response) => { console.log(`request.url=${request.url}`) response.end(`Hello World! times ${counter++}\n`) }) server.listen(port, () => console.log(`Listening on http://localhost:${port}`) )
13
![Page 14: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/14.jpg)
Running the server
14
![Page 15: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/15.jpg)
Taking a closer look
15
![Page 16: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/16.jpg)
Adding a bit of HTML style
This should work, right?
. ├── app │ └── index.js ├── index.js └── package.json
'use strict' const http = require('http')
const port = 3000 let counter = 0
const server = http.createServer((request, response) => { console.log(`request.url=${request.url}`) response.end(`<i>Hello World!</i> times ${counter++}\n`) }) server.listen(port, () => console.log(`Listening on http://localhost:${port}`) )
16
![Page 17: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/17.jpg)
Probably not as intended
17
![Page 18: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/18.jpg)
Correct header info is crucial
If we do not set an explicit Content-Type using the setHeader() method, the browser will assume it is ‘text/plain’, and that is rarely what we want
‘text/html’ is right for HTML documents
. ├── app │ └── index.js ├── index.js └── package.json
'use strict' const http = require('http')
const port = 3000 let counter = 0
const server = http.createServer((request, response) => { console.log(`request.url=${request.url}`) response.setHeader('Content-Type', 'text/html') response.end(`<i>Hello World!</i> times ${counter++}\n`) }) server.listen(port, () => console.log(`Listening on http://localhost:${port}`) )
18
![Page 19: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/19.jpg)
Taking a closer look again
19
![Page 20: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/20.jpg)
And in the Web browser…
20
![Page 21: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/21.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
21
![Page 22: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/22.jpg)
Input and Output on the Web
We have seen several example of getting data from a server to a Web browser
But how do you get data from a Web page back to the server?
this was solved, in the simplest form, in the early Web around HTML 2.0
There are two basic methods GET POST
22
![Page 23: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/23.jpg)
Input through GET
Very simple: http://…/foo.html?arg1=value&arg2=value&arg3=value (and so on)
Advantage you can make links that contain arguments to the server
Disadvantage there is a space limitation cannot be used for, e.g., file upload you would not want a password to be visible in the browser bar
23
![Page 24: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/24.jpg)
A more specific greeting
The standard Node ‘url’ module can parse the query
'use strict' const http = require('http') const url = require('url')
const port = 3000 let counter = 0 let recipient
const server = http.createServer((request, response) => { console.log(`request.url=${request.url}`) const query = url.parse(request.url, true).query if ('recipient' in query) { recipient = query['recipient'] } else { recipient = 'World' } response.setHeader('Content-Type', 'text/html') response.end(`<i>Hello ${recipient}!</i> times ${counter++}\n`) }) server.listen(port, () => console.log(`Listening on http://localhost:${port}`) )
24
![Page 25: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/25.jpg)
Input through POST
Requires a different HTTP method: POST
Data is transferred in the body of the request rather than the URL
you can send much more data because this data can be split up across several network packages, it is necessary to assemble the data at the server, before it can be processed the standard Node module ‘querystring’ can parse this kind of data
The data must be input through a <form> element on a Web page
25
![Page 26: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/26.jpg)
A HTML form
A form has a method and an action
There are many different kinds of input types
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Greetings for you</title> </head> <body> <form method="POST" action="http://localhost:3000/"> <div> <label for="recipient">Recipient:</label> <input type="text" id="recipient" name="recipient"> </div> <div> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> </div> <div> <button type="submit">Submit</button> </div> </form> </body> </html>
26
![Page 27: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/27.jpg)
Handling the POST data'use strict' const http = require('http') const querystring = require('querystring')
const port = 3000 const server = http.createServer((request, response) => { console.log(`request.url=${request.url}`) if (request.method === 'POST') { let body = '' request.on('data', (data) => { // got some data body += data }) request.on('end', () => { // all data received console.log(`request.body=${body}`) const post = querystring.parse(body) const recipient = post['recipient'] || 'World' const message = post['message'] || '' response.setHeader('Content-Type', 'text/html') response.end(`<div>Hello ${recipient}!</div> <div>${message}</div>\n`) }) } }) server.listen(port, () => console.log(`Listening on http://localhost:${port}`) )
27
![Page 28: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/28.jpg)
Filling a form
28
![Page 29: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/29.jpg)
Building a server with the http module
It is completely feasible to build a Web server based on the standard Node.js modules
However, as servers become more sophisticated, it requires a fair amount of manual labour
This is where a Web server framework can come in handy
There are many for Node.js, but the most popular is Express
29
![Page 30: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/30.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
30
![Page 31: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/31.jpg)
Adding Express to your project
npm install express --save installs the required modules in node_modules/ and updates package.json
add node_modules/ to .gitignore (you can install all the modules with ‘npm install’)31
![Page 32: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/32.jpg)
Hello World in Express
The basic case is not much shorter than with http, but the structure quickly becomes clearer, and we do not have to set the Content-Type header ourselves
'use strict' const express = require('express') const app = express() const port = 3000
app.get('/', (request, response) => { response.send('Hello World!') })
app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
32
![Page 33: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/33.jpg)
Chaining middleware in Express
Express functions by chaining middleware functions that take (request, response, next) as arguments, and each end by call the next() in line
'use strict' const express = require('express') const app = express() const port = 3000
app.use((request, response, next) => { request.timer = Date.now() next() }) app.use((request, response, next) => { console.log(`request.url=${request.url}`) next() }) app.get('/', (request, response) => { response.send('Hello World!') console.log(`Processing took: ${Date.now() - request.timer} ms`) }) app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
33
![Page 34: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/34.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
34
![Page 35: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/35.jpg)
Using a template engine: handlebars
It is extremely tedious and error prone to write HTML directly inside JavaScript
what if the design changes? no tool support for syntax etc
Most Web sites, while having many pages, will have relatively few layouts, wherein the content will vary
it is also tedious to have to repeat the same bits over and over in a static Web site
Template frameworks are made to support this kind of scenario:
define the layout once, mark the places where content should be inserted, configure your application, and everything works
35
![Page 36: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/36.jpg)
The layout: main.hbs
Standard HTML, except things inside {{{}}} will be replaced directly (HTML will not be escaped)
You can develop a Web page until satisfied, and then replace the changing bits with {{{}}}
app └── index.js views ├── hello.hbs └── layouts └── main.hbs
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Express handlebars !</title> </head> <body> {{{body}}} </body> </html>
36
![Page 37: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/37.jpg)
The view: hello.hbs
This is what replaces {{{body}}} in main.hbs
Things inside {{}} will have HTML escaped
app └── index.js views ├── hello.hbs └── layouts └── main.hbs
<h2>Hello {{recipient}}</h2>
37
![Page 38: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/38.jpg)
Configuring Express: index.js'use strict' const express = require('express') const exphbs = require('express-handlebars') const path = require('path') const app = express() const port = 3000
app.engine('.hbs', exphbs({ defaultLayout: 'main', extname: '.hbs', layoutsDir: path.join(__dirname, '../views/layouts') })) app.set('view engine', '.hbs') app.set('views', path.join(__dirname, '../views'))
app.get('/', (request, response) => { response.render('hello', { recipient: ‘World’// remember {{recipient}} in hello.hbs? }) })
app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
app └── index.js views ├── hello.hbs └── layouts └── main.hbs
38
![Page 39: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/39.jpg)
Handling input with Express
Express handles get input directly request.query.recipient (assuming http://…/somePage?recipient=Johnny)
POST data requires an additional module, such as body-parser (there are quite a few others)
npm install body-parser --save app.use(bodyParser.urlencoded({ extended: false })) // to add it to express
39
![Page 40: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/40.jpg)
The greeting: hello.hbs
Ought to be familiar from earlier
app └── index.js views ├── form.hbs ├── hello.hbs └── layouts └── main.hbs
<h2>Hello {{recipient}}</h2> <p>{{message}}</p>
40
![Page 41: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/41.jpg)
The form: form.hbs
Ought to be familiar from earlier
app └── index.js views ├── form.hbs ├── hello.hbs └── layouts └── main.hbs
<form method="POST" action="/"> <div> <label for="recipient">Recipient:</label> <input type="text" id="recipient" name="recipient"> </div> <div> <label for="message">Message:</label> <textarea id="message" name="message"></textarea> </div> <div> <button type="submit">Submit</button> </div> </form>
41
![Page 42: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/42.jpg)
The code: index.js 1/2
All this configures Express with body-parser and handlebars
app └── index.js views ├── form.hbs ├── hello.hbs └── layouts └── main.hbs
'use strict' const path = require('path') const express = require('express') const exphbs = require('express-handlebars') const bodyParser = require('body-parser') const app = express() const port = 3000
app.engine('.hbs', exphbs({ defaultLayout: 'main', extname: '.hbs', layoutsDir: path.join(__dirname, '../views/layouts') })) app.set('view engine', '.hbs') app.set('views', path.join(__dirname, '../views'))
app.use(bodyParser.urlencoded({ extended: false }))
42
![Page 43: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/43.jpg)
The code: index.js 2/2
This site handles both POST and GET data
app └── index.js views ├── form.hbs ├── hello.hbs └── layouts └── main.hbs
app.get('/', (request, response) => { response.render('form') }) app.post('/', (request, response) => { response.render('hello', { recipient: request.body.recipient, message: request.body.message }) }) app.get('/hello', (request, response) => { response.render('hello', { recipient: request.query.recipient, message: request.query.message }) }) app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
43
![Page 44: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/44.jpg)
In action, handling POST and GET
44
![Page 45: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/45.jpg)
Overview
The HTTP protocol The http module Input and output on the Web The Express framework HTML templates with handlebars Data-driven sites
45
![Page 46: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/46.jpg)
Data drives the Web
In practice, most Websites are database-driven
Layout, stylesheets, images, etc., are typically static resources and handled separately
The actual contents resides in databases, and are added into the Web pages as they are generated by the server
we will later be looking at Web pages that “build themselves” by retrieving data from servers
46
![Page 47: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/47.jpg)
Static resources
Everything found in the specified folder is served as http-server would have served it
the files in public/ are just ordinary HTML and CSS files
You can call app.use(express.static()) multiple times, if you need to serve static resources from different paths
app └── index.js public/ ├── index.html └── style └── main.css
'use strict' const express = require('express') const path = require('path') const app = express() const port = 3000
app.use(express.static(path.join(__dirname, '../public')))
app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
47
![Page 48: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/48.jpg)
Static resources
Note how the css file is down in a folder, but is correctly served nonetheless by express.static
app └── index.js public/ ├── index.html └── style └── main.css
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Static pages!</title> <link rel="stylesheet" type="text/css" media="screen" href="style/main.css" /> </head> <body> <h1>Hello World!</h1> <p>This is just a static page, served up by Express, and referencing a stylesheet, served in a similar manner.</p> </body> </html>
body { background-color: palegreen; }
48
![Page 49: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/49.jpg)
Databases!
I believe you have heard of them?
In a Web context, databases largely come in two kinds: SQL databases, such as MySQL, PostgreSQL, SQLite, DB2, Oracle, … NoSQL databases, such as MongoDB, CouchDB, Cassandra, Redis, …
Given your expertise with SQL, we will stick with SQL…
49
![Page 50: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/50.jpg)
The data driven Greetings site
Let’s create a site, where users can add their own greeting to a list of greetings
50
![Page 51: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/51.jpg)
What should the site support?
GET /form the form with which to create a greeting
POST /greetings create a new greeting
GET /greetings/:id get a specific greeting
GET /greetings get all greetings
DELETE /greeting/:id delete a specific greeting
51
![Page 52: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/52.jpg)
Redirecting
A Web server can instruct a Web browser to another page through the Location: field in the HTTP header
This can be done from Express with the method redirect()
We will use that to automatically direct users from / to /form
52
![Page 53: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/53.jpg)
Let’s start with a simpler version
When starting out, it can be simpler to take things step by step, e.g., starting out with an in-memory array before building the full database-backed version
Once that works, it will provide us with the skeleton for the full version
53
![Page 54: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/54.jpg)
The code: index.js 1/2app └── index.js public └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
'use strict' const path = require('path') const express = require('express') const exphbs = require('express-handlebars') const bodyParser = require('body-parser') const app = express() const port = 3000 const greetings = []
app.engine('.hbs', exphbs({ defaultLayout: 'main', extname: '.hbs', layoutsDir: path.join(__dirname, '../views/layouts') })) app.set('view engine', '.hbs') app.set('views', path.join(__dirname, '../views'))
app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(path.join(__dirname, '../public')))
54
![Page 55: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/55.jpg)
The code: index.js 1/2app └── index.js public └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
app.get('/', (request, response) => { response.redirect('/form') }) app.get('/form', (request, response) => { response.render('form') }) app.post('/greetings', (request, response) => { const greeting = {recipient: request.body.recipient, message: request.body.message} greetings.push(greeting) response.redirect('/greetings') }) app.get('/greetings', (request, response) => { response.render('greeting', { greetings: greetings }) }) app.get('/greetings/:id', (request, response) => { const id = request.params.id const greeting = greetings[id] response.render('greeting', { greetings: [greeting] }) }) app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
55
![Page 56: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/56.jpg)
Greetings with Handlebars
With #each, Handlebars can iterate through arrays of objects, accessing the fields within the objects
app └── index.js public └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
{{#each greetings}} <h2>Hello {{recipient}}</h2> <p>{{message}}</p> {{/each}}
<div><a href="/form">Add a greeting</a></div>
56
![Page 57: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/57.jpg)
The site in action
57
![Page 58: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/58.jpg)
Moving on to a proper database
We need a proper database
You already know MySQL, so let’s try something new!
By far the most widespread SQL database system in the world is SQLite
it is very compact (~700 kB), fast, and implements most of SQL-92 it is not very good with concurrent writes, but concurrent reads are fine it is found almost everywhere, from desktop computers (handles, e.g., Mail on macOS) to Web browsers to mobile phones (your contact list is almost certainly in SQLite) some idiot lecturer forgot to install it on the Raspberry Pi image—see Resources for instructions (and the node-sqlite3 module installer is slow on RPi, sorry about that)
But, once installed it is easy to administer58
![Page 59: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/59.jpg)
SQLite3 in action
59
![Page 60: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/60.jpg)
The database: db.js 1/2
We open the database file, and if our table does not exist, we create it using standard SQL
app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
const sqlite3 = require('sqlite3').verbose() const path = require('path') const dbName = path.join(__dirname, 'greetings.sqlite') const db = new sqlite3.Database(dbName)
db.serialize(() => { const sql = ` CREATE TABLE IF NOT EXISTS greetings (id integer primary key, recipient, message TEXT) ` db.run(sql) })
60
![Page 61: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/61.jpg)
The database: db.js 2/2class Greetings { static all (callback) { db.all('SELECT * FROM greetings', callback) }
static find (id, callback) { db.get('SELECT * FROM greetings WHERE id = ?', id, callback) }
static create (greeting, callback) { const sql = 'INSERT INTO greetings(recipient, message) VALUES (?, ?)' db.run(sql, greeting.recipient, greeting.message, callback) }
static delete (id, callback) { if (!id) { return callback(new Error('Please provide an id')) } db.run('DELETE FROM greetings WHERE id = ?', id, callback) } }
module.exports = db module.exports.Greetings = Greetings
app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
61
![Page 62: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/62.jpg)
The code: index.js 1/2'use strict' const path = require('path') const express = require('express') const exphbs = require('express-handlebars') const bodyParser = require('body-parser') const Greetings = require('../db/db').Greetings const app = express() const port = 3000
app.engine('.hbs', exphbs({ defaultLayout: 'main', extname: '.hbs', layoutsDir: path.join(__dirname, '../views/layouts') })) app.set('view engine', '.hbs') app.set('views', path.join(__dirname, '../views')) app.use(bodyParser.urlencoded({ extended: true }))
app.use(express.static(path.join(__dirname, '../public')))
app.get('/', (request, response, next) => { response.redirect('/form') })
app.get('/form', (request, response, next) => { response.render('form') })
app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
62
![Page 63: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/63.jpg)
The code: index.js 2/2app.post('/greetings', (request, response, next) => { const greeting = {recipient: request.body.recipient, message: request.body.message} Greetings.create(greeting, (err, greeting) => { if (err) return next(err) response.redirect('/greetings') }) }) app.get('/greetings', (request, response, next) => { Greetings.all((err, greetings) => { if (err) return next(err) response.render('greeting', { greetings: greetings }) }) }) app.get('/greetings/:id', (request, response, next) => { const id = request.params.id Greetings.find(id, (err, greeting) => { if (err) return next(err) response.render('greeting', { greetings: [greeting] }) }) }) app.listen(port, (err) => { if (err) return console.error(`An error occurred: ${err}`) console.log(`Listening on http://localhost:${port}/`) })
app └── index.js db ├── db.js └── greetings.sqlite public ├── js │ └── main.js └── style └── main.css views ├── form.hbs ├── greeting.hbs └── layouts └── main.hbs
63
![Page 64: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/64.jpg)
The site in action
64
![Page 65: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/65.jpg)
Note on the use of databases
You should always let the module handle data that you have no control over
e.g., something that has been entered into a form and sent over the network
This is correct, because it allows the module to sanitise the input
This is wrong and dangerous, do not do this:
const sql = 'INSERT INTO greetings(recipient, message) VALUES (?, ?)' db.run(sql, greeting.recipient, greeting.message, callback)
const bad = `INSERT INTO greetings(recipient, message) VALUES (${greeting.recipient}, ${greeting.message})` db.run(bad)
65
![Page 66: JavaScript on the Serverbouvin/itWoT/2019/slides/JavaScript on the Server.pdf · ./index.js It is considered good coding style to let the central index.js be sparse, and let the actual](https://reader031.vdocuments.site/reader031/viewer/2022041018/5ecca91fa0af283cb576d380/html5/thumbnails/66.jpg)
Wrap-up
Node.js comes into its own, once we start server programming
With Express and its associates modules, it is possible to create functional Web sites in relatively little code
Templates alone will save you a lot of time
Proper databases of course makes everything better
66