🎣 Supabase Webhooks:自動化驅動資料庫

在傳統的後端開發中,當「資料發生變化」時要觸發其他動作,往往非常麻煩。

例如:「當使用者註冊成功時,寄一封歡迎信給他。」 傳統做法是:

  1. 前端呼叫 /api/register
  2. 後端在 API 裡面寫:db.users.insert(...)
  3. 如果 Insert 成功,後端接著呼叫寄信程式碼 sendWelcomeEmail(...)
  4. 如果寄信失敗,還要處理重試邏輯。

這種做法的問題是:如果我們有一天開了另一個 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。

  1. 登入 Supabase 後台。
  2. 在左側選單點選 Database -> Webhooks
  3. 點擊右上角的 Create a new Hook
  4. 設定條件:
    • Name: Order Paid Webhook
    • Table: 選擇 orders
    • Events: 勾選 Update (因為我們是監聽狀態更新)
  5. 設定 HTTP 請求:
    • Method: POST
    • URL: 填入你的 API 網址 (如果是在本地開發,你需要用 Ngrok 把 localhost 暴露到公網,例如 https://your-ngrok.app/api/webhooks/order-paid)
  6. 設定 HTTP Headers:
    • Content-type: application/json
    • (強烈建議) 加上一個 Secret Token:Authorization: Bearer my-super-secret-token
  7. 點擊 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)」。這就是打造大型擴展性系統的終極奧義!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!