---
tags: Node.js 直播班 - 2023 春季班
---
# 第二週 - Node.js NPM 整合 MongoDB
1. 錄影
2. 提醒關於之後的錄影檔都會如何存放
3. [Adobe XD 設計稿](https://xd.adobe.com/view/c0763dbe-fc15-42e8-be0b-8956ed03e675-9525/grid)
4. [講義簡報](https://whimsical.com/mongoose-YKTKL5aF4pHQbkemgQTMZH)
5. [六角後端鬼故事傳奇 00:00~10:00](https://courses.hexschool.com/courses/node-js-20223/lectures/45500860)
## 講解大綱
* Mongoose 講解
* Schema 設計
* 何謂 Model?
* 第二週主線任務設計
## 公布最終作業使用者故事
1. 此為 NFT 會員制的動態牆,僅有限量 VIP 可以加入討論
2. 在 NFT 合約上,有限制永久只有 500 位會員參與此平台
## NPM
* dotenv:環境變數
* [Mongoose](https://mongoosejs.com/)
## 常見 Mongoose 指令
### 連線語法
```=javascript
const http = require('http');
const mongoose = require('mongoose');
mongoose
.connect("mongodb://localhost:27017/testPost")
.then(() => console.log('資料庫連接成功'));
// schema 開始
// schema 結束
const requestListener = async(req, res)=>{
res.end();
}
const server = http.createServer(requestListener);
server.listen(3000);
```
> mongodb://localhost:27017/posts
### 建立 Schema
```=javascript
const postSchema = new mongoose.Schema(
{
content: {
type: String,
required: [true, 'Content 未填寫']
},
image: {
type:String,
default:""
},
createdAt: {
type: Date,
default: Date.now(),
select: false
},
name: {
type: String,
required: [true, '貼文姓名未填寫']
},
likes: {
type:Number,
default:0
}
}
);
const Post = mongoose.model('Post', postSchema);
const init = async()=>{
const AllPost = await Post.find();
console.log(AllPost)
}
init();
```
### CURD 語法
* 新增:[Model.create()](https://mongoosejs.com/docs/api/model.html#model_Model.create)
* 刪除:[Model.findByIdAndDelete()](https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndDelete)
* 更新:[Model.findByIdAndUpdate()](https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate)
* 查詢:[Model.find()](https://mongoosejs.com/docs/api/model.html#model_Model.find)
```
await Post.findByIdAndUpdate(id,editContent) // 更新單筆
await Post.deleteMany({}); //刪除全部
await Post.findByIdAndDelete(id); // 刪除單筆
```
:::spoiler 完整 Code
## code
```=JavaScript
const http = require('http');
const mongoose = require('mongoose');
mongoose
.connect("mongodb://localhost:27017/testPost")
.then(() => console.log('資料庫連接成功'));
const postSchema = new mongoose.Schema(
{
content: {
type: String,
required: [true, 'Content 未填寫']
},
image: {
type:String,
default:""
},
createdAt: {
type: Date,
default: Date.now(),
select: false
},
name: {
type: String,
required: [true, '貼文姓名未填寫']
},
likes: {
type:Number,
default:0
}
}
);
const Post = mongoose.model('post', postSchema);
const requestListener = async(req, res)=>{
const headers = {
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PATCH, POST, GET,OPTIONS,DELETE',
'Content-Type': 'application/json'
}
let body = "";
req.on('data', chunk=>{
body+=chunk;
})
if(req.url=="/posts" && req.method == "GET"){
const post = await Post.find();
res.writeHead(200,headers);
res.write(JSON.stringify({
"status": "success",
post
}));
res.end();
}else if(req.url=="/posts" && req.method == "POST"){
req.on('end',async()=>{
try{
const data = JSON.parse(body);
if(data.content !== undefined){
const newPost = await Post.create(
{
name: data.name,
content: data.content,
}
);
res.writeHead(200,headers);
res.write(JSON.stringify({
"status": "success",
"data": newPost,
}));
res.end();
}else{
res.writeHead(400,headers);
res.write(JSON.stringify({
"status": "false",
"message": "欄位未填寫正確,或無此 todo ID",
}));
res.end();
}
}catch(error){
res.writeHead(400,headers);
res.write(JSON.stringify({
"status": "false",
"message": error,
}));
res.end();
}
})
}else if(req.url.startsWith("/posts/") && req.method=="DELETE"){
const id = req.url.split('/').pop();
await Post.findByIdAndDelete(id);
res.writeHead(200,headers);
res.write(JSON.stringify({
"status": "success",
"data": null,
}));
res.end();
}else if(req.method == "OPTIONS"){
res.writeHead(200,headers);
res.end();
}else{
res.writeHead(404,headers);
res.write(JSON.stringify({
"status": "false",
"message": "無此網站路由"
}));
res.end();
}
}
const server = http.createServer(requestListener);
server.listen(3000);
```
:::
## 本堂作業
設計一個 /posts 路由,設計與 [todolist API 練習](https://discord.com/channels/801807326054055996/1073411249926324234/1075786895914700820) 一樣的設計,並將欄位調整擴充為一則貼文會有的欄位,可觀看第二週直播錄影做為參考。
- 將程式碼上傳 GitHub
#### 加分條件
- 請觀看此[範例](https://github.com/gonsakon/nodeweek2-sample),拉到 Model 資料夾來設計
#### **作業分享**
1. 作業:請提供 **GitHub Repo** 連結,並回報到此 [Discord](https://discord.com/channels/801807326054055996/1073411249926324234/1088723579752943637) 作業分享區
### Render 部署 Node 伺服器
* [Render 伺服器部署 Node.js](https://israynotarray.com/other/20221213/3036227586/)
## Callback 介紹
請寫一篇部落格,解釋何謂 **callback function**,加分題是講解 express middleware 如何透過 callback function 實現
* [Genos - https://stark920.github.io/2022/04/17/JScallback/](https://stark920.github.io/2022/04/17/JScallback/)
* [邱繼緯(小麥)0.5本 - 了解 JavaScript 中的 callback function](https://ayugioh2003.github.io/2022/04/callback-function/)
* [Han Lai 0.5本 - 淺談 JavaScript Callback Function](https://hackmd.io/@laihan/S1nTlwY49)
* [Pon - 0.5本 - 回呼函式(callback function)](https://forest-lunge-74a.notion.site/callback-function-da5ef9fa618b437384e622e19b2d97ea)
## Mock API 介紹
請寫一篇部落格,講解你跟後端合作的時候,用哪個 Mock API 服務,造福大家
* [Albert(炸豆腐) - Mock API 身為前端該了解的知識點 ](https://callum.hashnode.dev/mock-api)
* [joe.chang - 別等了!用Mock Service Worker來產生Mock API吧!](https://medium.com/coding-hot-pot/%E5%88%A5%E7%AD%89%E4%BA%86-%E7%94%A8mock-service-worker%E4%BE%86%E7%94%A2%E7%94%9Fmock-api%E5%90%A7-f921ab30d5c7)
* [Leo Chuang - 使用Mocks Server快速建立Front-End mock API](https://medium.com/@dennis302218905/%E4%BD%BF%E7%94%A8mocks-server%E5%BF%AB%E9%80%9F%E5%BB%BA%E7%AB%8Bfront-end-mock-api-130a1876832b)