iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Modern Web

現在就學Node.js系列 第 18

MongoDB + Express 打造 CRUD API - Day18

  • 分享至 

  • xImage
  •  

為什麼要把 CRUD 包裝成 API?

昨天我們學會了直接用 MongoDB Driver 操作資料,
例如 insertOnefindupdateOnedeleteOne

雖然能操作基本的CURD,但在實際開發時會有兩個問題:

  1. 安全性問題
    任何人都能對資料庫做讀寫、刪除,風險超高(資料外洩、惡意操作)。
  2. 缺乏商業邏輯
    專案中常需要額外處理,例如:
    - 權限檢查(誰能修改、誰能刪除)
    - 資料驗證(必填欄位、格式檢查)
    - 業務規則(例如新增訂單前先檢查庫存)

因此最佳做法是 透過後端 API 封裝 CRUD 操作,前端只需發送請求。

CRUD 與 RESTful API 的對應

在 RESTful 設計裡,每個 CRUD 對應到一個 API:

動作 MongoDB 典型 API 狀態碼
Create insertOne POST /users 201 Created
Read(列表) find GET /users 200 OK
Read(單筆) findOne GET /users/:id 200 / 404
Update updateOne PUT /users/:id 200 / 404
Delete deleteOne DELETE /users/:id 204 / 404

這樣設計清楚直觀,也符合業界慣例。

📝 範例程式:Express + MongoDB

下面是我們的 API 實作,包含了 資料驗證防呆處理

import express from "express";
import { MongoClient, ObjectId } from "mongodb";

const app = express();
const url = "mongodb://localhost:27017";
const client = new MongoClient(url);

app.use(express.json()); // 解析 JSON body

// 初始化 MongoDB 連線
let users;
async function connectDB() {
  await client.connect();
  const db = client.db("testdb");
  users = db.collection("users");
}
connectDB();

// ➕ Create
app.post("/users", async (req, res) => {
  const { name, age } = req.body;

  if (!name || typeof name !== "string") {
    return res.status(400).json({ error: "姓名必填,且必須是字串" });
  }
  if (age && typeof age !== "number") {
    return res.status(400).json({ error: "年齡必須是數字" });
  }

  const result = await users.insertOne({ name, age, createdAt: new Date() });
  res.status(201).json({ message: "新增成功", id: result.insertedId });
});

// 📖 Read (全部)
app.get("/users", async (req, res) => {
  const allUsers = await users.find().toArray();
  res.json(allUsers);
});

// 📖 Read (單筆)
app.get("/users/:id", async (req, res) => {
  try {
    const user = await users.findOne({ _id: new ObjectId(req.params.id) });
    if (!user) return res.status(404).json({ error: "找不到該使用者" });
    res.json(user);
  } catch {
    res.status(400).json({ error: "ID 格式不正確" });
  }
});

// ✏️ Update
app.put("/users/:id", async (req, res) => {
  try {
    const { name, age } = req.body;
    if (!name && !age) {
      return res.status(400).json({ error: "必須提供要更新的欄位" });
    }

    const result = await users.updateOne(
      { _id: new ObjectId(req.params.id) },
      { $set: { ...(name && { name }), ...(age && { age }) } }
    );

    if (result.matchedCount === 0) {
      return res.status(404).json({ error: "找不到該使用者" });
    }

    res.json({ message: "更新成功" });
  } catch {
    res.status(400).json({ error: "ID 格式不正確" });
  }
});

// ❌ Delete
app.delete("/users/:id", async (req, res) => {
  try {
    const result = await users.deleteOne({ _id: new ObjectId(req.params.id) });

    if (result.deletedCount === 0) {
      return res.status(404).json({ error: "找不到該使用者" });
    }

    res.json({ message: "刪除成功" });
  } catch {
    res.status(400).json({ error: "ID 格式不正確" });
  }
});

app.listen(3000, () =>
  console.log("🚀 API 運行中:http://localhost:3000")
);

測試 API

使用 cURL 或 Postman 測試:

# 新增 (Create)
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "age": 25}'

# 查詢所有 (Read)
curl http://localhost:3000/users

# 查詢單筆 (Read by ID)
curl http://localhost:3000/users/<id>

# 更新 (Update)
curl -X PUT http://localhost:3000/users/<id> \
  -H "Content-Type: application/json" \
  -d '{"age": 30}'

# 刪除 (Delete)
curl -X DELETE http://localhost:3000/users/<id>

小結

今天我們完成了:

  1. Express + MongoDB 建立完整的 CRUD API
  2. 加上 簡單的資料驗證(避免亂資料)
  3. 增加 防呆判斷(找不到資料時回應 404)
  4. 學會如何用 RESTful API 對應資料庫操作

前端可以直接呼叫 API 來操作 MongoDB,而不是直接連線資料庫,這樣的架構更安全、更符合真實專案需求。明天要接續學習新的內容-Mongoose。


上一篇
MongoDB 之 CRUD 操作 — Create、Read、Update、Delete - Day17
下一篇
Mongoose 入門 — 更高效的 MongoDB 操作工具 - Day 19
系列文
現在就學Node.js24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言