Express.js Basic CRUD Todo list

Express.js 入門造個簡單的增刪查改待辦事項

前言

近期正在學習後端相關領域,與前端最貼近的框架絕對就是 Express.js🔗 了,很適合目標為全端的工程師,因為可以使用單一語言最快接觸到兩種領域。

express-fastify-koa-restify-trend
Express.js 長年下載量最高

這次實作並不會牽扯到資料庫相關的部分,侷限在使用 Express.js 打造一個簡單的 in-memory 後端伺服器,熟悉一下開設增刪查改相關 API。

建構最小可運作 Express.js

基本環境 Node.js🔗、開發編輯器就不多介紹了,先開個新資料夾初始化 NPM 專案並安裝 Express.js。

Terminal window
# npm 初始化 (-y 為全部採用預設值不再逐一設定)
$ npm init -y
# 安裝 Express
$ npm install express

接著只要創建一個新的 app.js 撰寫 Express.js 相關代碼並用 Node.js 執行 (node app.js)即可,以下為最小可運作 Express.js。

  • req 為 request 慣例縮寫
  • res 為 response 慣例縮寫
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

我們將 Express 公開在 3000 通訊埠上,因此只要打開瀏覽器到本機的對應位置 http://localhost:3000/ 就能看到 Hello World 範例。Express 有許多方法可以調用,翻閱官方文件了解更多像是:app.get()🔗res.send()🔗

建構增刪查改 API

一、規劃

Express.js 的 API 非常直觀,對應 HTTP 請求方法🔗 可以到官方文件找同名的方法,可以期望建構一個代辦事項我們會需要:

  1. todos 資料初始化於 Express 開啟時
  2. 開設 API 供外部與資料互動
  3. 撰寫資料增刪查改相關邏輯
let todos = [
// {
// id: 1, (不重複的識別碼)
// title: 'Init Todo',
// isCompleted: false,
// },
];
// 取得 Todos
app.get();
// 新增 Todos
app.post();
// 修改 Todos
app.put();
// 刪除 Todos
app.delete();

有一點要留意是 Express 預設不會解析 res.body 為 JSON 格式,我們可以透過 Middleware🔗 來預設處理所有 res.body 使用 express.json🔗

app.use(express.json());

二、實作

剩下就是撰寫伺服器內部邏輯與資料互動,我習慣獨立計算出一個新的 todos 再把內容重新指派回去,中途留意一些例外狀況像是編輯不存在的 ID 需要返回客戶端錯誤。

// Get All Todos
app.get("/api/todos", (req, res) => {
res.json(todos)
})
// Create Todo
app.post("/api/todos", (req, res) => {
const { title } = req.body;
if (!title || title.trim() === "") {
return res.status(400).json({ error: "Title is required and cannot be empty." });
}
const newTodo = {
id: Date.now().toString(),
title,
isCompleted: false
}
todos = [...todos, newTodo]
res.status(201).json(newTodo)
})
// Edit {id} Todo
app.put("/api/todos/:id", (req, res) => {
const targetId = req.params.id
const targetIndex = todos.findIndex(todo => todo.id === targetId);
const isTargetTodoExist = targetIndex !== -1
if (!isTargetTodoExist) {
return res.status(404).json({ error: "Todo not found" });
}
const updatedTodo = {
...todos[targetIndex],
title: req.body.title ?? todos[targetIndex].title,
isCompleted: req.body.isCompleted ?? todos[targetIndex].isCompleted
}
const newTodos = [
...todos.slice(0, targetIndex),
updatedTodo,
...todos.slice(targetIndex + 1),
];
todos = newTodos
res.json(updatedTodo)
})
// Delete {id} Todo
app.delete("/api/todos/:id", (req, res) => {
const targetId = req.params.id
const targetIndex = todos.findIndex(todo => todo.id === targetId);
const isTargetTodoExist = targetIndex !== -1
if (!isTargetTodoExist) {
return res.status(404).json({ error: "Todo not found" });
}
const newTodos = [
...todos.slice(0, targetIndex),
...todos.slice(targetIndex + 1),
]
todos = newTodos
res.status(204).send();
})

三、檢查

熱門的 API 檢測工具有:Postman🔗Hoppscotch🔗Insomnia🔗,可以選一套順手的進行檢測,我偏好 Hoppscotch 並且之前也有寫過相關推薦文章:從圖片學習使用 Hoppscotch 來打出 API🔗。可以創建一個群組將這次專案相關的 API 都儲存起來方便管理。

Hoppscotch API 群組

補充:CORS 設定

Access to fetch at 'http://localhost:3000/api/todos' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

如在前端專案遇到以上 CORS 錯誤訊息可以參考我寫過的另一篇文章:寫給網頁開發者的 CORS 理解🔗

如字面意思來說可以透過替伺服器回應設置允許的 Access-Control-Allow-Origin 來准許不同通訊埠的網站也能使用該 API,這裡我依靠設置 Express.js 一款 Middleware:cors🔗來解決。

Terminal window
$ npm install cors

安裝完成後添加該 Middleware 並設置開放 origin 即可:

app.use(cors({ origin: 'http://127.0.0.1:5500' }));

補充:使用 Nodemon 及時重啟伺服器

如果開發時更動後需要反覆的重啟伺服器很麻煩可以嘗試看看 Nodemon🔗

Nodemon 用來在開發 Node.js 應用程式時自動監測檔案變更並重新啟動伺服器,避免手動停止並重新啟動伺服器的麻煩。如下添加為 NPM Script🔗 以後可以執行 npm run dev 就能開啟即時更新開發伺服器了。

"scripts": {
"dev": "nodemon app.js"
}

總結

這次實作專案:in-memory-todo🔗 的檔案有放上 GitHub,後續額外實作了前端的部分。如果對前端感興趣可以參考我的另一篇文章:JavaScript 五個步驟製作代辦事項🔗 基本就是在此基礎上接上後端的資料,並更動迴避了一些 XSS🔗 的隱患。

延伸閱讀