### Intro. to Node.js + Express + HTTP

Spring 2019 。 Ric Huang
---
### Recap: Backend Server
* 還記得這幾次的上課與作業我們都是使用 "create-react-app" 這個工具,自動產生整個 React App 的架構,然後利用 "npm start" 來執行 app.
* 但你有沒有好奇過為什麼我們執行 app 是去打開 "localhost:3000", 而不是用 browser 打開 "index.html"?
* 你有沒有發現只要你更新任何程式碼,都會自動 trigger "localhost:3000" 的更新?
----
### Your First Backend Server
* Basically, 當你執行 "npm start" 之後,你就啟動了一個 "node.js server", 跑在你的電腦 (i.e. localhost) 上,並且透過 port 3000 來跟外界溝通。
* 而伺服器(server)上面的後端服務(backend service)會一直傾聽相關的需求,例如:發現程式碼有更新,就啟動重新編譯的動作,並刷新服務程式
----
### Toward a complete web service
* 一個完整的網路服務應用通常要有一個後台(backend),並且有資料庫(database)來儲存用戶或者是服務流程中的相關資料
* 而上述的服務通常會放在一個有固定 IP 的伺服器,並且去申請 domain name, 讓客戶端(client)可以透過 URL(e.g. 網址) 來取得服務
----
### Localhost as a local web server
* 開發者在開發網路服務的時候,為了測試方便,會先使用自己的電腦來作為後端的伺服器,這就是為什麼會有在開發的時候 backend 與 frontend 都在同一台機器(i.e. localhost)的現象。等到開發到一定程度之後再來 deploy 到雲端 or 機房的機器上面。
----
基本上任何可以跑在伺服器(電腦)的程式語言都可以用來實現後端的 server

----
### 我們接下來要介紹目前用 JavaScript 做後端最流行的框架:Node.js
---
## Introduction to Node.js
* 由 [Ryan Dahl](https://en.wikipedia.org/wiki/Ryan_Dahl) 在 2009 開發,目標在實現 "JavaScript Everywhere" 的典範,讓 web development 可以統一在一個語言底下
* 基本上 Node.js 就是一個 JavaScript 的 runtime environment, **讓 JavaScript 可以在 Browser 以外的環境執行**,所以 Node.js 可以用來開發以 JS 為基礎的後端程式
----
### Node.js 的核心就是 Google 開源的 Chrome V8 JS 引擎 (in C++)

----
**Node.js** 雖然有 .js, 但它並不是指某個單一的 JS 檔案
* 事實上,它是個 JS runtime environment, 允許新增各種用 JS 寫的功能模組 (modules),包含了 file system I/O, networking, binary data (buffers), cryptography functions, data streams, etc.
----
### Node and npm
* 它的各種模組可以透過像是 "npm" (Node Package Management, introduced in 2010) 等工具來管理,而且由於它開源的關係,Node 相關的套件正在以非常驚人的速度在增加
----
[](http://www.modulecounts.com/)
----
[](http://www.modulecounts.com/)
----
### 所以,面對現實,如果你要學 Web Programming, 然後只想 focus 在一個語言,那學 JavsScript 準沒錯,而且 Node.js 是必學!
----
### 非常活躍的 Node.js Project

* 下星期 v12 就要出來了!
----
### Node.js Versions
* 每六個月有個 Major Release (四月、十月)
* 奇數版本在十月份發行的時候,先前在四月發行的偶數版本就會自動變成 Long Term Support (LTS) 版本, 然後會被積極、持續地維護 18 個月,結束後會被延長維護 12 個月,然後就壽終正寢
* 奇數版本沒有 LTS
----
### Node.js is singly-threaded, event-driven, and non-blocking I/O
----
### Recall: Blocking vs. Non-blocking

----
* 因為 Node.js 是 singly-threaded, 然後又讓要花較多時間的 I/O 利用 non-blocking 的方式來溝通,因此,可以同時 handle 成千上萬個 events, 而不會有一般平行程式會遇到的 context switching 的 overhead.
* 而這些 non-blocking tasks 通常是透過 callback functions 來告知主程式任務完成,並且利用 event loop 來做到非同步 (asynchronous) 的排程,也就是說,事件之間不會有事先固定的順序,而是按照實際完成的先後順序來處理,可以盡量省下事件之間互相等待的情形
---
### Node.js Modules
* 最早 Node.js 是 follow **CommonJS** 的標準來實踐 modules, 但近來已逐漸使用 **ECMAScript Specification** 作為 default
----
### CommonJS 規範
* CommonJS 的誕生是為了要讓眾多的 JS modules 有一個共同的標準,得以彼此共生在 browser 以外的不同環境底下,建立應用生態系
* 主要包含了 模組規範、套件規範、I/O、File System、Promise 等
* Node.js 就是 CommonJS 的一個主要實踐者
----
### CommonJS 是在 runtime 加載(require) modules
```javascript
let { stat, exists, readFile } = require('fs');
const math = require('math');
```
* 然後就可以用了:
```javascript
console.log(math.add(1, 2));
```
* 至於輸出模組,則用 "exports.functionName"
```javascript
exports.incrementOne = function (num) {
return math.add(num, 1);
};
```
----
### ES6 則是強調「靜態時」就要決定模組的相依性
* By **"export"** & **"import"**
```javascript
export var firstName = 'Michael';
export function multiply(x, y) { return x * y; } as MM;
export class MyClass extends React.Component...;
```
* from 後面的 path 可以是絕對或是相對位址; '.js' 可省
```javascript
import { foo } from './myApp.js';
import { aVeryLongName as someName } from '../someFile.js'
```
----
### "export default"
* 在前面的例子當中,使用者需要知道 import 進來的檔案裡頭原先的那些變數、function、class 的名字為何,需要跟原來檔案裡頭定義的名字一樣,才可以使用
* **"export default"** 則讓我們可以不用管原來檔案裡頭這些function/class 叫什麼名字,甚至是可以 anonymous
```javascript
// Add.js
export default (a, b) => (a+b);
```
----
* 不過既然 function/class 都可以 anonymous 了,所以:
1. export 的檔案就只能有一個 "export default" 的 function or class
2. 在 import 時的名字是屬於 import 那個檔案的 scope
3. from 後面的檔案名稱可以把 .js 省略
```javascript
// import an (anonymous) function from "Add.js"
// The name of the function is (re-)named to "MyAdd"
import MyAdd from "Add";
```
----
### 講了那麼多,那我要怎麼開使使用 Node.js 以及他的 modules 呢?
[Example] Save the following as a file "test.js"
```javascript
const http = require('http');
const PORT = process.env.PORT || 3000;
const server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World!');
});
server.listen(PORT, function() {
console.log(`Server listening on: http://localhost:${PORT}`);
});
```
* 然後執行 "node test.js"
* 打開 "localhost:3000" 看看!
----
* 當然,你也可以直接用 node 的 command line.
* 只要在 terminal 鍵入 "node", 你就可以試用各種 Node.js 的指令與模組了! (Use **.help** to get help)
----
### 我們往後會再教大家如何建立 Node.js server, 現在先來學一個 Node.js 的 "web framework" --- **Express.js**
---
### Learning "Express.js" [[ref](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction)]
* (sudo) npm install -g express
* (sudo) npm install -g express-generator
----
### My first Express App
* express express-test1
* It will create an "express app" called "express-test1"
* cd express-test1
* npm install
* npm start
* go to "localhost:3000"
----
* 不過,跟 **create-react-app** 不同的是,當你修改檔案的時候,上頁的 node server 並不會自動重啟
* 解決方式:**npm install nodemon --save**
* Note: "--save" to save the dependencies to **package.json**
* 開啟 **package.json**, 在 "scripts" 裡頭,加上 **"devstart": "nodemon ./bin/www"**, 然後重啟 server by **"npm run devstart"**
----
### The "express-test1" directory
* **./bin/www**: node 開始執行的 script
* **./app.js**: 定義 server
* **./routes/**: 定義 application 的 routing
* **./views**: 定義 application 的 viewing template
----
### ./bin/www
```javascript
var app = require('../app');
var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
```
----
### ./app.js
```javascript
var express = require('express');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(...); // middlewares
module.exports = app; // for ./bin/www
```
----
### ./routes/index.jx
```javascript
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
```
* For viewing template, see "./views/index.jade"
```javascript
extends layout
block content
h1= title
p Welcome to #{title}
```
----
### ./routes/users.js
```javascript
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;
```
* What does the above mean? (also check "app.js")
Try [http://localhost:3000/users/](http://localhost:3000/users/)
* 試試看改一下檔案,讓 [http://localhost:3000/users/ric](http://localhost:3000/users/ric) 印出 "Ric is cool!"
----
## Huh?
### 現在是在講什麼?
---
### 試想一下,你有一個用 Node/Express 寫的 Blog service, 放在某個 web server (say, https://www.myblog.com), 你打開 browser, 鍵入上述網址,理應你會載入該網址的 "index.html", 並且同時載入該 HTML 裡頭用到的 JavaScript files.
----
### 然後你在你看到的前端 Blog 網頁 (well, 就是 "index.html") click on 作者連結、文章連結等,理論上前端就會送一些資料(e.g. 作者/文章 ID)給後端,然後後端收到之後,就會回傳對應的資料(e.g. 作者資訊/文章內容)回來
----
### 你有沒有想過...
### 前端與後端是用什麼管道與方式來聯絡的呢?
---
### HTTP
* HyperText Transfer Protocol
* HTTP Client 跟 Server 之間進行請求與回應的標準
* Version: 1.0 (1996), 2.0 (2015), 3.0 (2018)
----
### Client Request and Server Response

----
### URL Structure

----
### HTTP Request
* A Request-line
* Zero or more header fields followed by CR/LF
* An empty line (i.e., a line with nothing preceding the CR/LF)
* Optionally a message-body
----
### HTTP Request Example
```htmlmixed
POST /users/123 HTTP/1.1 // Request-line
Host: www.example.com // header fields
Accept-Language: en-us // ..
Connection: Keep-Alive // ..
// empty line
licenseID=string&content=string&/paramsXML=string // message-body
```
----
### 常見的 HTTP Request Methods [[ref](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)]
* **GET** --- The GET method requests a representation of the specified resource. Requests using GET should only retrieve data.
* **POST** --- The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server.
* **PUT** --- The PUT method replaces all current representations of the target resource with the request payload.
* **DELETE** --- The DELETE method deletes the specified resource.
----
### 其他 HTTP Request Methods [[ref](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)]
* **HEAD** --- The HEAD method asks for a response identical to that of a GET request, but without the response body.
* **PATCH** --- The PATCH method is used to apply partial modifications to a resource.
----
### An Analogy of HTTP Request Methods [[ref](https://data-sci.info/2015/10/24/%E5%B8%B8%E8%A6%8B%E7%9A%84http-method%E7%9A%84%E4%B8%8D%E5%90%8C%E6%80%A7%E8%B3%AA%E5%88%86%E6%9E%90%EF%BC%9Agetpost%E5%92%8C%E5%85%B6%E4%BB%964%E7%A8%AEmethod%E7%9A%84%E5%B7%AE%E5%88%A5/)]
假設現在我們要點餐,
我們必須先知道菜單是甚麼(get),
我們會向服務生點餐(post),
我們想要取消剛才點的餐點(delete),
我們想要重新點一次(put),
我們想要加點甜點和飲料(patch)。
----
### HTTP Response
* A Status-line
* Zero or more header fields followed by CR/LF
* An empty line (i.e., a line with nothing preceding the CR/LF)
* Optionally a message-body
----
### HTTP Response Example
```htmlmixed
HTTP/1.1 200 OK // Status-line
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Connection: keep-alive
Content-Length: 53
Content-Type: text/html; charset=UTF-8
Date: Tue, 11 Oct 2016 16:32:33 GMT
ETag: "53-1476203499000"
Last-Modified: Tue, 11 Oct 2016 16:31:39 GMT
// empty line
<html> // message-body
<body>
<h1>Hello, World!</h1>
</body>
</html>
```
---
### Express 定義了各種 middlewares
### 來協助 client 與 server 進行溝通
Express is a **routing** and **middleware** web framework that has minimal functionality of its own: An Express application is essentially a series of middleware function calls.
----
Middleware functions are functions that have access to the **request object (req)**, the **response object (res)**, and the **next** middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.
----
### Middleware Calling Stack

----
### Types of Middleware in Express
* Application-level middleware
* Router-level middleware
* Error-handling middleware
* Built-in middleware
* Third-party middleware
----
### 基本的 Express Middlewares
```javascript
var express = require('express');
var app = express();
app.use([path,] callback [, callback...]);
app.METHOD(path, callback [, callback ...]);
// METHOD can be:
// checkout copy delete get head lock
// merge mkactivity mkcol move m-search
// notify options patch post purge put
// report search subscribe trace unlock
// unsubscribe
```
----
### 如果 path 沒有指定,則每次都會被執行
```javascript
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
```
----
### 如果指定 path, 則只有 routing path 符合的時候才會被執行
```javascript
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
```
----
### 可以有多個 callback functions
```javascript
app.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
```
----
### 如果沒有 next(), 則執行完就會結束這個 request-response cycle
```javascript
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
// This will never be called!
app.get('/user/:id', function (req, res, next) {
res.end(req.params.id)
})
```
----
### 也可以傳 array of callbacks
```javascript
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod(req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('User Info')
})
```
---
### 今天先教到這邊
* 這部分要學好,只有上課聽 + practice 是不夠的
* 下次上課前請先自行完成以下的 pre-readings:
* Express 官網關於 Middleware 以及 APIs 的描述:https://expressjs.com/
* MDN Express tutorials:[[link](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website)]
* 另外,下次上課前也請了解一下 Promise/Async/Await
* MDN [[Using Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)] [[Promsie](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)]
* MDN [[async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)]
----
### 是的,這週沒有 practice or homework
### 請務必自行研讀上述的 materials, 或者是找尋其他相關的 online tutorials
---
## See you next week!
{"metaMigratedAt":"2023-06-14T21:08:38.435Z","metaMigratedFrom":"YAML","title":"Introduction to Node.js + Express + HTTP (04/17)","breaks":true,"slideOptions":"{\"theme\":\"beige\",\"transition\":\"fade\",\"slidenumber\":true}","contributors":"[{\"id\":\"752a44cb-2596-4186-8de2-038ab32eec6b\",\"add\":15633,\"del\":1673},{\"id\":\"a202b02b-072b-4474-be14-c5a3f8932dbb\",\"add\":38,\"del\":0}]"}