cover

所有應用程式,拆到最底層就是四個動作:新增、讀取、更新、刪除。

先講結論

不管你做的是社群平台、電商網站、還是一個待辦清單 App,背後都離不開 CRUD——Create、Read、Update、Delete。聽起來簡單?確實,但它就像料理的煎煮炒炸,所有複雜的功能拆到最底層都是這四個動作的排列組合。

操作SQLHTTP Method
Create 新增INSERTPOST
Read 讀取SELECTGET
Update 更新UPDATEPUT/PATCH
Delete 刪除DELETEDELETE

CRUD 遇上 RESTful API

為什麼要特別提 HTTP Method?因為前端跟後端溝通時,CRUD 的每個操作都有一個「約定俗成」的 HTTP Method 對應。搞清楚這層對應,你讀任何 API 文件都會很順。

POST   /api/users           // 新增使用者
GET    /api/users           // 取得所有使用者
GET    /api/users/123       // 取得特定使用者
PUT    /api/users/123       // 更新使用者資料
DELETE /api/users/123       // 刪除使用者

軟刪除 vs 硬刪除:你真的要刪掉它嗎?

這是實務上最常遇到的設計抉擇之一。

硬刪除就是 DELETE FROM users WHERE id = 1,資料直接從資料庫消失,無法復原。

軟刪除是加一個 deleted_at 欄位,標記為已刪除但資料還在:

-- 軟刪除
UPDATE users SET deleted_at = NOW() WHERE id = 1;
 
-- 查詢時排除已刪除的
SELECT * FROM users WHERE deleted_at IS NULL;
 
-- 想反悔?
UPDATE users SET deleted_at = NULL WHERE id = 1;

商業應用幾乎都用軟刪除。原因很實際:資料可追溯可復原、符合法規(GDPR 之類的有資料保留期限要求)、避免關聯資料的完整性問題。你以為使用者按了「刪除」就真的刪掉了?大多數時候只是把它藏起來而已。

三個不能偷懶的最佳實踐

資料驗證 —— 在執行 CRUD 之前,一定要驗證輸入。相信前端傳過來的資料是乾淨的?你太天真了。

const userSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
});
const validatedData = userSchema.parse(inputData);

權限控制 —— 使用者只能操作自己有權限的資料。我見過有人寫的 API,只要你知道別人的 ID 就能改別人的資料。這不是 feature 是 bug

const post = await prisma.post.findUnique({ where: { id: postId } });
if (post.authorId !== currentUser.id) {
  throw new Error('Unauthorized');
}

交易處理 —— 轉帳場景:A 扣了錢但 B 沒收到,這種事情不能發生。多個 CRUD 操作要嘛全成功、要嘛全失敗。

await prisma.$transaction(async (tx) => {
  await tx.account.update({
    where: { id: fromId },
    data: { balance: { decrement: amount } },
  });
  await tx.account.update({
    where: { id: toId },
    data: { balance: { increment: amount } },
  });
});

CRUD 跟其他概念的關係

搞混 CRUD、RESTful API、HTTP Methods 的人很多。其實它們是不同層面的東西:CRUD 描述「做什麼」(新增 / 讀取 / 更新 / 刪除),RESTful API 定義「怎麼設計介面」,HTTP Methods 是「怎麼傳輸」。三者緊密結合,但不是同一件事。


CRUD 是程式設計的柴米油鹽,不性感但沒有它什麼菜都炒不出來。

延伸閱讀

RESTful API 設計最佳實踐 RESTful API API 概念 資料庫基礎