<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測試工具 ![](https://i.imgur.com/hlaHXEX.png) 推薦大家都可以使用此工具進行測試 安裝後在隨處建立一個檔案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,希望能夠對大家有所幫助,謝謝!