<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
</style>
# Mongoose使用教學 快速上手MongoDB+Express
## 前言
Mongoose 是 MongoDB 的套件,可以讓我們更方便處理 CRUD。
本教學將以其顯易懂的方式教大家上手,此次將以TODOLIST為範例製作後端。
而在開始前請大家確定自己已經安裝了MongoDB喔!
如果還沒有安裝可以點選以下文章查看安裝教學
<a href='https://pjchender.dev/database/mongo-install/'>MongoDB安裝教學</a>
## 第一步-安裝
初始化專案
```
npm init
```
安裝開發工具 express以及mongoose
```
npm i express mongoose
```
安裝開發者使用工具
nodemon則是能夠使我們Ctrl + S 儲存專案時能及時重啟伺服器 不用每次都要手動輸入指令重啟
```
npm i --save-dev nodemon
```
將專案中的
```javascript=
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
```
替換成
```javascript=
"scripts": {
"server" : "nodemon app.js"
},
```
其中的app.js不一定要是app.js可以用自己喜歡的名字即可,並不固定。
## 第二步-設定app.js
新建一個app.js並在裡面新增以下資訊
```javascript=
///引入套件
const express = require('express');
const app = express();
const mongoose = require("mongoose");
///與資料庫連線
mongoose.connect("mongodb://localhost/user");
const db = mongoose.connection;
///與資料庫連線發生錯誤時
db.on('err',err => console.log(err));
///與資料庫連線成功連線時
db.once('open' , () => console.log('connected to database'));
///重要!! 要加這行才可以讓req.json讀的到資料
app.use(express.json());
///引入Router 一個Router基本上處理一個資料表
const todoRouter = require("./routes/todo");
///此處的/todo代表連線到該Router的基本路徑為 http://localhost:3000/todo
app.use("/todo",todoRouter);
///設定Server的Port
app.listen(3000 , () => console.log("server started"))
```
而後在終端機輸入
```
npm run server
```
這時候會發現程式報錯了,這是因為我們在20行引入的路由還沒有新增。
我們先新增資料夾routes
並在裡面新增todo.js
用來存放關於todo相關的資料庫操作指令
在裡面寫入
```javascript=
//引入套件
const express = require("express");
const router = express.Router();
//Export 該Router
module.exports = router
```
就完成伺服器的建立,並成功建立一個Route了。
## 第三步-測試API功能
我們將資料夾routes中的todo做一下更改
```javascript=
//引入套件
const express = require("express");
const router = express.Router();
//發出Hello world
router.get("/", (req, res) => {
///回傳訊息Helllo World
res.send("Hello World");
})
//Export 該Router
module.exports = router
```
這時候我們在VsCode可以安裝一個很棒的API測試工具

推薦大家都可以使用此工具進行測試
安裝後在隨處建立一個檔案test.rest
並在裡面放入以下程式碼
```
###檢視Hello World
GET http://localhost:3000/todo
```
這使GET的上方就會出現Send Request 點擊後
應該就會成功跳出視窗並看到顯示的Hellow World
而之後新增的每個指令之間都要加###用以區隔喔!
## 第四步-新增資料表並檢視資料
我們在根目錄新增一個models資料夾並新增todo.js設置資料表
並在裡面放入以下程式碼
```javascript=
///引入套件
const mongoose = require('mongoose');
///該資料表的格式設定
const todoSchema = new mongoose.Schema({
thing:{ //事項名稱
type:String, //設定該欄位的格式
required:true //設定該欄位是否必填
},
isDone:{ //是否已完成
type:Boolean,
required:true,
default:false //設定預設值
},
createdDate:{ //新增時的時間
type: Date,
default: Date.now,
required:true
},
})
//匯出該資料表
module.exports = mongoose.model("todo" , todoSchema);
```
而後再routes/todo.js中將程式碼更改為以下
```javascript=
//引入套件
const express = require("express");
const router = express.Router();
const Todo = require("../models/todo");
//取得全部資料
//使用非同步 才能夠等待資料庫回應
router.get("/", async (req, res) => {
//使用try catch方便Debug的報錯訊息
try {
//找出Todo資料資料表中的全部資料
const todo = await Todo.find();
//將回傳的資訊轉成Json格式後回傳
res.json(todo);
} catch (err) {
//如果資料庫出現錯誤時回報 status:500 並回傳錯誤訊息
res.status(500).json({ message: err.message })
}
})
//Export 該Router
module.exports = router
```
這時候我們在操作一次test.rest應該會發現他回傳了空陣列,
這是因為我們還沒有在裡面新增資料,
那麼我們下一步就來新增資料吧。
## 第五步-在資料表中新增資料
我們將routes/todo.js的程式碼改為以下
```javascript=
//引入套件
const express = require("express");
const router = express.Router();
const Todo = require("../models/todo");
//取得全部資料
//使用非同步 才能夠等待資料庫回應
router.get("/", async (req, res) => {
//使用try catch方便Debug的報錯訊息
try {
//找出Todo資料資料表中的全部資料
const todo = await Todo.find();
//將回傳的資訊轉成Json格式後回傳
res.json(todo);
} catch (err) {
//如果資料庫出現錯誤時回報 status:500 並回傳錯誤訊息
res.status(500).json({ message: err.message })
}
})
//新增代辦事項
//將Method改為Post
router.post("/", async (req, res) => {
//從req.body中取出資料
const todo = new Todo({
thing:req.body.thing,
isDone:req.body.isDone,
});
try {
//使用.save()將資料存進資料庫
const newtodo = await todo.save();
//回傳status:201代表新增成功 並回傳新增的資料
res.status(201).json(newtodo);
} catch (err) {
//錯誤訊息發生回傳400 代表使用者傳入錯誤的資訊
res.status(400).json({ message : err.message })
}
})
//Export 該Router
module.exports = router
```
並把test.rest中的程式碼改為以下
```
###檢視全部事項
GET http://localhost:3000/todo
###新增代辦事項
POST http://localhost:3000/todo
Content-Type:application/json
{
"thing":"好酷的事情1",
"isDone":false
}
```
並執行新增代辦事項可以發現成功新增了,
要注意在body中的json最後一個事項不能加,
以該範例來說就是 "isDone":false 的後面不能加, 不然會發生格式錯誤喔!
這時候我們在執行檢視全部事項,就會看到所新增的事項了喔!
## 第六步-在資料表中檢視特定資料
我們在routes/todo.js中新增關於檢視特定資料的方式
```javascript=
//檢視特定事項
//在網址中傳入id用以查詢
router.get("/:id", async (req, res) => {
try {
//等待資料庫回傳該id的資料
const todo = await Todo.findById(req.params.id);
if (todo == undefined) {
//如果無該資料則回傳404找不到資料
return res.send(res.status(404).json({ message: "Can't find todo" }))
}
//有該資料則回傳
res.send(todo)
} catch (err) {
//資料庫出現錯誤則回傳500 並回傳錯誤訊息
return res.send(res.status(500).json({ message: err.message }))
}
})
```
我們先使用檢視全部事項能夠取得資料
並將其中的id複製下來
```javascript=
//也就是
{
"_id": "62c3d797bf44b33a7cac6403",
"thing": "好酷的事情1",
"isDone": false,
"createdDate": "2022-07-05T06:17:59.840Z",
"__v": 0
}
//中的 62c3d797bf44b33a7cac6403
```
並在test.rest新增一個新的方式
```
GET http://localhost:3000/todo/62c3d797bf44b33a7cac6403
```
就可以取得該代辦事項的資訊
而我們在刪除、更新資料時都會需要執行此段程式以檢查是否有該事項,
因此我們來把它來寫成一個Function吧!
我們將檢視特定事項的程式碼改為以下 並新增一個Function
```javascript=
//檢視特定事項
//在網址中傳入id用以查詢
////會先執行getTodo後才繼續裡面的內容
router.get("/:id", getTodo , (req, res) => {
//取出res.todo並回傳
res.send(res.todo);
})
//檢視是否有該代辦事項
async function getTodo(req,res,next) {
let todo;
try {
todo = await Todo.findById(req.params.id);
if (todo == undefined) {
return res.status(404).json({ message:"Can't find todo" })
}
} catch (err) {
return res.status(500).json({ message:err.message })
}
//如果有該事項 則將他加入到res中
res.todo = todo
//在router中執行middleware後需要使用next()才會繼續往下跑
next();
}
```
這時候再去test.rest執行檢視特定事項一樣可以正確執行喔!
## 第六步-在資料表中刪除特定資料
我們在routes/todo.js中新增以下route
```javascript=
//刪除代辦事項
//先使用getTodo取得該代辦資訊
router.delete("/:id", getTodo, async (req, res) => {
try {
//將取出的待辦事項刪除
await res.todo.remove();
//回傳訊息
res.json({ message:"Delete todo succeed" })
} catch (err) {
//資料庫操作錯誤將回傳500及錯誤訊息
res.status(500).json({ message:"remove todo faild" })
}
})
```
而後在test.rest即可使用
```
DELETE http://localhost:3000/todo/62c3d797bf44b33a7cac6403
```
刪除待辦事項
## 第七步-在資料表中更新特定資料
我們在routes/todo.js中新增以下route
```javascript=
//更新待辦事項
//先使用getTodo取得資訊
router.patch("/:id", getTodo , async (req, res) => {
//檢測使用者是否有傳入事項 有的話則將getTodo回傳的thing改成使用者傳入的thing
if (req.body.thing != null) {
res.todo.thing = req.body.thing
}
//檢測使用者是否有傳入以完成資訊 有的話則將getTodo回傳的isDone改成使用者傳入的isDone
if (req.body.isDone != null) {
res.todo.isDone = req.body.isDone
}
try {
//將資訊存入資料庫
const updateTodo = await res.todo.save();
//回傳更改後的資料給使用者
res.json(updateTodo);
} catch (err) {
//資料庫更新錯誤回傳400及錯誤訊息
res.status(400).json({ message:"Update User failed" }) //更新失敗
}
})
```
而後再test.rest中使用
```
PATCH http://localhost:3000/todo/62c3d8f1018e3fd3363f1ff1
Content-Type:application/json
{
"thing":"好酷的事情2ˇ3",
"isDone":true
}
```
就可以成功更改內容了!
## 結語
以上的內容成功教導了各位如何操作簡單的CRUD,希望能夠對大家有所幫助,謝謝!