昨天我把 Todo API 接上了 PostgreSQL,讓資料可以真正存進資料庫。
不過 API 還有一個大問題:如果出錯,沒有好好處理,整個伺服器就會回傳一個很醜的錯誤訊息,甚至直接掛掉。
今天的重點就是補上 錯誤處理 和 日誌紀錄,讓 API 更穩健。
想像以下情境:
這些情況如果沒有被捕捉,就會變成 500 Internal Server Error,使用者完全不知道發生什麼事。
正確做法是:捕捉錯誤 → 回傳清楚的訊息 → 記錄 Log。
Express 提供一個特別的 Middleware,只要有 (err, req, res, next)
這四個參數,就能捕捉錯誤。
app.use((err, req, res, next) => {
console.error("發生錯誤:", err.message);
res.status(500).json({ error: "伺服器錯誤,請稍後再試" });
});
這樣一來,只要在程式裡用 next(err)
或丟出錯誤,Express 都會進到這個錯誤處理器。
Log(紀錄)可以幫助我們在開發與維運時追蹤問題。
最簡單的做法是用 morgan:
npm install morgan
import morgan from "morgan";
app.use(morgan("dev"));
這樣每次請求都會輸出像這樣的 log:
GET /todos 200 15 - 2.345 ms
POST /todos 201 42 - 3.112 ms
延續昨天的 app.js
,我幫 Todo API 加上錯誤處理:
import express from "express";
import { pool } from "./db.js";
import morgan from "morgan";
const app = express();
const port = 3000;
app.use(express.json());
app.use(morgan("dev"));
// GET /todos
app.get("/todos", async (req, res, next) => {
try {
const result = await pool.query("SELECT * FROM todos ORDER BY id ASC");
res.json(result.rows);
} catch (err) {
next(err); // 把錯誤丟給錯誤處理器
}
});
// POST /todos
app.post("/todos", async (req, res, next) => {
try {
const { task } = req.body;
if (!task) {
return res.status(400).json({ error: "task 欄位必填" });
}
const result = await pool.query(
"INSERT INTO todos (task) VALUES ($1) RETURNING *",
[task]
);
res.status(201).json(result.rows[0]);
} catch (err) {
next(err);
}
});
// 錯誤處理 Middleware
app.use((err, req, res, next) => {
console.error("❌ 發生錯誤:", err.stack);
res.status(500).json({ error: "伺服器錯誤,請稍後再試" });
});
app.listen(port, () => {
console.log(`伺服器啟動:http://localhost:${port}`);
});
curl -X POST http://localhost:3000/todos \
-H "Content-Type: application/json" \
-d '{}'
輸出:
{ "error": "task 欄位必填" }
把 SELECT * FROM todos
改成 SELECT * FROM not_exist
→ 伺服器會輸出:
❌ 發生錯誤: error: relation "not_exist" does not exist
而客戶端只會收到:
{ "error": "伺服器錯誤,請稍後再試" }
這樣一來,使用者不會看到內部細節,但我們還是能在 log 裡看到真實的錯誤。
今天最大的收穫就是:API 不只是能跑,還要能「優雅地出錯」。
到目前為止,我的 Todo API 已經有 CRUD、能連資料庫、能處理錯誤與紀錄 log。