在上一篇文章中,我們初步建立了 MongoDB Atlas 的帳號,並且快速認識了 Mongoose,接下來當然就是要來整合再一起啦~
首先一開始的一些起手式我就不再次說明了,直接附上指令讓你可以快速建立一個專案:
mkdir example-express-mongoose
cd example-express-mongoose
npm init -y
npm i express mongoose
touch index.js
理論上來說,上面的指令你應該沒有任何一行看不懂的,如果有,那就代表你沒有認真看前面的文章,建議你可以先回去看看~
那麼我們這次要寫什麼呢?其實很簡單,把前面寫的範例程式碼把它整合到一起就好了,所以我們先來看看前面的程式碼:
const express = require('express');
const app = express();
const data = [];
app.use(express.json());
app.get('/todos', (req, res) => {
res.send(data);
});
app.post('/todos', (req, res) => {
const { title, completed } = req.body;
data.push({
id: new Date().getTime(),
title: cacheBody.title,
completed: cacheBody.completed,
});
res.send(data);
});
app.listen(3000)
接下來,補上我們前一篇所使用的 Mongoose 的程式碼:
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// ...略過其他程式碼
mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
.then(() => console.log('Connected to MongoDB...'))
// ...略過其他程式碼
app.listen(3000)
當你在終端機輸入 npm start
or node index.js
後,你會看到 Mongoose 已經幫你連上 MongoDB Atlas 了囉~
這時候有些人應該會有個好奇的疑問點...
「如果我在 Mongoose 連接成功之前,就已經有人來存取我的 API 了,那不是會出錯嗎?」
這個問題問得很好,確實是有這種可能性會發生,如果我們的專案在成功連接之前,就先接受了 API 請求,而剛好這些 API 請求需要操控資料庫的話,那麼確實是會出錯沒有錯。
那麼我們該怎麼解決這個麻煩的問題呢?其實我們只需要善加利用前面所學的 Middleware 就可以了,我們可以在 Middleware 中判斷是否已經連接成功,如果沒有的話,就回傳一個錯誤訊息,如果有的話,就繼續執行下一個 Middleware。
const express = require('express');
const mongoose = require('mongoose');
const app = express();
async function connectMongoDB () {
try {
await mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
console.log('Connected to MongoDB...')
connectStatus = true;
} catch (error) {
console.log(error)
}
}
connectMongoDB()
// ...略過其他程式碼
app.use(express.json());
app.use((req, res, next) => {
if (connectStatus) {
next();
} else {
res.status(503).send({
status: false,
message: 'Server is not ready'
});
}
})
// ...略過其他程式碼
app.listen(3000)
這樣子我們就可以避免在 Mongoose 還沒跟 MongoDB Atlas 連接成功之前,就先接受 API 請求的問題囉!
在準備跟 MongoDB 互動(ex:CRUD)之前,我們必須先定義 Schema,這樣子才能夠讓 Mongoose 知道我們要存取的資料長什麼樣子。
const todoSchema = new mongoose.Schema({
id: Number,
title: String,
completed: Boolean,
});
Note
CRUD 意指 Create、Read、Update、Delete,也就是我們常說的「增刪改查」。
那麼什麼是 Schema 呢?Schema 是用來定義 MongoDB 結構的一種概念,主要說明了這些資料的型別、欄位名稱、預設值等等,這樣子我們才能夠在存取資料時,知道該怎麼去存取。
因此以剛剛的範例程式碼來講,我們定義了一個 todoSchema,裡面有兩個欄位,分別是 title
跟 completed
,而這兩個欄位的型別分別是 String 跟 Boolean。
那麼只要是這些 Schema 屬性之外的資料,都會被忽略,也就是說,如果我們在存取資料時,傳入了一個多餘的欄位,那麼這個欄位就會被忽略,不會被存到資料庫中。
透過 Schema 可以確保我們資料的一致性和完整性。
定義完 Schema 之後,接著就是要來建立 Model,通常 Modal 會對應特定的 Schema,而且每個 Model 都會跟資料庫中的一個 collection 對應,也就是說,我們可以透過 Model 來存取資料庫中的資料。
const todoSchema = new mongoose.Schema({
id: Number,
title: String,
completed: Boolean,
});
const Todo = mongoose.model('Todo', todoSchema);
你也可以把 mongoose.model
當作 new
實例化的概念沒有錯,因為它們的用法很像,只是 mongoose.model
是用來建立 Model 的,而 new
則是用來建立實例的。
透過這個 Modal 我們就可以針對資料庫做 CRUD(增刪改查)的動作囉!
那麼目前我們完整程式碼長怎樣呢?我們先來看一下
const express = require('express');
const mongoose = require('mongoose');
const app = express();
let connectStatus = false;
async function connectMongoDB () {
try {
await mongoose.connect('mongodb+srv://example:example@cluster0.zyfzacs.mongodb.net/?retryWrites=true&w=majority')
console.log('Connected to MongoDB...')
connectStatus = true;
} catch (error) {
console.log(error)
}
}
connectMongoDB()
const data = [];
app.use(express.json());
app.use((req, res, next) => {
if (connectStatus) {
next();
} else {
res.status(503).send({
status: false,
message: 'Server is not ready'
});
}
})
const todoSchema = new mongoose.Schema({
id: Number,
title: String,
completed: Boolean,
});
const Todo = mongoose.model('Todo', todoSchema);
app.get('/todos', (req, res) => {
res.send(data);
});
app.post('/todos', (req, res) => {
const { title, completed } = req.body;
data.push({
id: new Date().getTime(),
title: cacheBody.title,
completed: cacheBody.completed,
});
res.send(data);
});
app.listen(3000)
我們第一個要做的事情是先來做 Post
的動作,也就是寫入資料庫
const express = require('express');
// ...略過其他程式碼
app.post('/todos', async (req, res) => {
const { title, completed } = req.body;
const todo = new Todo({
id: new Date().getTime(),
title,
completed,
});
await todo.save();
res.send({
status: true,
message: 'Create todo successfully',
});
});
// ...略過其他程式碼
app.listen(3000)
其實我們可以發現實作難度真的不高,只要透過 new
來建立一個實例,然後透過 save
來存入資料庫就可以了。
取得 Todo 資料的話更簡單了,只需要透過 find
就可以取得資料庫中的資料囉!
const express = require('express');
// ...略過其他程式碼
app.get('/todos', async (req, res) => {
const todos = await Todo.find();
res.send({
status: true,
data: todos,
});
});
// ...略過其他程式碼
app.listen(3000)
基本上以上程式碼你就可以透過 Postman 來測試了,不意外的話,當你打完 Post /todos
之後,你會發現資料庫中多了一筆資料,接著你在打 Get /todos
之後,你會得到以下資料
{
"status": true,
"data": [
{
"_id": "64e08a11f5e1d20be8115fb0",
"id": 1692437009750,
"title": "Hello",
"completed": false,
"__v": 0
}
]
}
恭喜你,基本的新增與讀取的功能完成囉!
剩下的編輯與刪除的部分,我也想保留給你作為小功課,你可以試著自己實作看看,這邊我也補充相關資源讓你可以參考
以上資源我相信可以幫助你做出來的。
那麼這邊你應該會好奇 __v
與 _id
這兩個屬性是什麼東西,這邊我也做一下補充。
這兩個屬性是 MongoDB 預設的屬性,_id
是 MongoDB 的主鍵(key),而 __v
則是版本鍵(version key),比較需要注意的是 _id
是 ObjectId,而不是我們一般的數字。
如果你希望不要有這兩個屬性,你可以透過 select
來過濾掉
const todos = await Todo.find().select('-__v -_id');
那麼這一篇差不多就先到這些結束,我們下一篇見。
終於來到第十一天了,其實我發現我身上的存稿真的不是很夠 Orz
而且我現在人正在日本發文唷~