🎣 Supabase Webhooks:自動化驅動資料庫
在傳統的後端開發中,當「資料發生變化」時要觸發其他動作,往往非常麻煩。
例如:「當使用者註冊成功時,寄一封歡迎信給他。」 傳統做法是:
- 前端呼叫
/api/register。 - 後端在 API 裡面寫:
db.users.insert(...)。 - 如果 Insert 成功,後端接著呼叫寄信程式碼
sendWelcomeEmail(...)。 - 如果寄信失敗,還要處理重試邏輯。
這種做法的問題是:如果我們有一天開了另一個 API /api/admin/create-user,或是老闆直接進資料庫後台手動新增使用者,那這些使用者就收不到歡迎信了,因為邏輯寫死在某支 API 裡面。
有沒有一種方法,是「不管誰動了這張表,只要有新資料,資料庫就自己去觸發寄信」? 有!這就是 Database Webhooks (資料庫觸發器)!
1. 什麼是 Database Webhooks?
在 PostgreSQL 的底層,有一個非常強大的功能叫做 Trigger (觸發器)。它可以監聽某張表發生的 INSERT, UPDATE, DELETE 事件。
Supabase 團隊把這個底層功能包裝了起來,做成了非常易用的 Database Webhooks。 它的運作邏輯是: 當 [這張表] 發生 [新增/修改/刪除] 時,自動發送一個 [HTTP POST 請求] 到 [你指定的網址]。
這把原本封閉在資料庫內部的事件,瞬間解放到了整個網際網路!
2. 實戰情境:訂單狀態更新通知
假設我們有一張 orders 表,裡面有一個欄位是 status (可能為 pending, paid, shipped)。
我們希望:「只要訂單狀態被改為 paid,就打 API 通知老闆出貨 (或是串接 Line Notify)」。
步驟 1:撰寫接收 Webhook 的 API (你的 Next.js)
首先,在我們的 Next.js 專案裡,準備一支用來收信的 API:
// src/app/api/webhooks/order-paid/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
// 1. 取得 Supabase 傳過來的 Webhook Payload
const body = await request.json();
// Supabase Webhook 傳來的資料格式大概長這樣:
// {
// "type": "UPDATE",
// "table": "orders",
// "record": { "id": "123", "status": "paid", "amount": 500 }, // 新資料
// "old_record": { "id": "123", "status": "pending", "amount": 500 } // 舊資料
// }
const newRecord = body.record;
const oldRecord = body.old_record;
// 2. 判斷邏輯:確保真的是「從別的狀態變成 paid」
if (newRecord.status === 'paid' && oldRecord.status !== 'paid') {
console.log(`🎉 收到付款通知!訂單號:${newRecord.id},金額:${newRecord.amount}`);
// TODO: 這裡你可以呼叫寄信服務 (Resend) 或是 Line Notify!
// sendLineNotify(`訂單 ${newRecord.id} 已付款,趕快出貨!`);
}
return NextResponse.json({ success: true });
} catch (error) {
return NextResponse.json({ error: 'Webhook 處理失敗' }, { status: 500 });
}
}
步驟 2:在 Supabase 後台設定 Webhook
現在,我們要告訴 Supabase 把事件推送到這支 API。
- 登入 Supabase 後台。
- 在左側選單點選 Database -> Webhooks。
- 點擊右上角的 Create a new Hook。
- 設定條件:
- Name:
Order Paid Webhook - Table: 選擇
orders表 - Events: 勾選
Update(因為我們是監聽狀態更新)
- Name:
- 設定 HTTP 請求:
- Method:
POST - URL: 填入你的 API 網址 (如果是在本地開發,你需要用 Ngrok 把 localhost 暴露到公網,例如
https://your-ngrok.app/api/webhooks/order-paid)
- Method:
- 設定 HTTP Headers:
- Content-type:
application/json - (強烈建議) 加上一個 Secret Token:
Authorization: Bearer my-super-secret-token
- Content-type:
- 點擊 Save hook。
3. 資安防護:如何驗證 Webhook 的真假?
這是一件非常可怕的事:你的這支 /api/webhooks/order-paid 是公開在網路上的!
如果有一個駭客猜到這個網址,他可以用 Postman 自己發一個 POST 請求過去,裡面塞滿假的 {"status": "paid"} 訂單資料,你的系統就會瘋狂以為有人付錢了並狂出貨!
防禦方法:驗證 Secret Token
在 Supabase 設定 Webhook 時 (步驟 6),我們偷偷塞了一個自定義的 HTTP Header:
Authorization: Bearer my-super-secret-token
現在,回到我們的 Next.js 程式碼,加上警衛:
// src/app/api/webhooks/order-paid/route.ts
export async function POST(request: Request) {
// 🛡️ 資安第一道防線:檢查金鑰
const authHeader = request.headers.get('Authorization');
if (authHeader !== `Bearer ${process.env.SUPABASE_WEBHOOK_SECRET}`) {
console.error("駭客攻擊!Webhook 驗證失敗!");
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// 金鑰正確,繼續執行後續出貨邏輯...
const body = await request.json();
// ...
}
這樣一來,只有知道這把 SUPABASE_WEBHOOK_SECRET 的 Supabase 伺服器,才能成功叩開你的大門!
4. Edge Functions vs Webhooks
你可能會問,Supabase 本身也有提供 Serverless 函數 (Edge Functions),這跟 Webhook 有什麼不同?
- Edge Functions:程式碼託管在 Supabase 上 (Deno 環境)。適合寫一些非常輕量的計算邏輯。
- Database Webhooks:把事件推播出去。通常推薦把事件推到你自己的 Next.js 專案裡 (Node.js 環境)。
實戰建議: 如果你是使用 Next.js App Router 的全端開發者,強烈建議使用 Webhooks 推回你的 Next.js API。 因為你的 Next.js 裡已經有了完整的 ORM (Prisma/Drizzle)、已經有你寫好的寄信模組、支付模組。與其在 Supabase Edge Functions 裡面重新寫一套邏輯,不如把所有業務邏輯都集中在 Next.js 裡管理,開發體驗會流暢好幾倍!
掌握了 Database Webhooks,你的架構就從「被動查詢」升級成了「事件驅動 (Event-Driven)」。這就是打造大型擴展性系統的終極奧義!