Supabase Edge Functions 開發實戰指南
Supabase Edge Functions 開發實戰指南
Supabase Edge Functions 是基於 Deno 運行的 serverless 函式,部署在全球各地的邊緣節點,讓 API 邏輯可以在距離使用者最近的伺服器上執行。這篇文章介紹如何從零開始開發和部署 Edge Functions。
環境設定
# 安裝 Supabase CLI
npm install -g supabase
# 初始化專案
supabase init
# 登入
supabase login
# 連結到你的 Supabase 專案
supabase link --project-ref your-project-ref建立第一個 Edge Function
supabase functions new hello-world這會建立 supabase/functions/hello-world/index.ts:
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
Deno.serve(async (req: Request) => {
const { name } = await req.json()
const data = {
message: `Hello ${name}!`,
}
return new Response(
JSON.stringify(data),
{ headers: { "Content-Type": "application/json" } },
)
})本地開發與測試
# 啟動本地 Supabase 環境
supabase start
# 啟動 Edge Functions 本地伺服器
supabase functions serve hello-world --env-file .env.local
# 測試
curl -i --location --request POST http://localhost:54321/functions/v1/hello-world \
--header 'Authorization: Bearer YOUR_ANON_KEY' \
--header 'Content-Type: application/json' \
--data '{"name":"台灣"}' 存取 Supabase 資料庫
import { createClient } from 'jsr:@supabase/supabase-js@2'
Deno.serve(async (req: Request) => {
// 從請求 header 取得 JWT
const authHeader = req.headers.get('Authorization')!
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{ global: { headers: { Authorization: authHeader } } }
)
// 查詢資料(會套用 RLS 規則)
const { data: articles, error } = await supabase
.from('articles')
.select('id, title, published_at')
.eq('is_published', true)
.order('published_at', { ascending: false })
.limit(10)
if (error) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 400, headers: { "Content-Type": "application/json" } }
)
}
return new Response(
JSON.stringify({ articles }),
{ headers: { "Content-Type": "application/json" } }
)
})使用 Service Role(繞過 RLS)
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '',
)
// 這個 client 可以讀寫所有資料,不受 RLS 限制
// 只在有必要時使用,注意安全性處理 Webhook
一個常見的用途是接收第三方 Webhook:
import { createClient } from 'jsr:@supabase/supabase-js@2'
import { crypto } from "jsr:@std/crypto"
const WEBHOOK_SECRET = Deno.env.get('STRIPE_WEBHOOK_SECRET') ?? ''
async function verifyStripeWebhook(payload: string, signature: string): Promise<boolean> {
const parts = signature.split(',');
const timestamp = parts.find(p => p.startsWith('t='))?.split('=')[1];
const sig = parts.find(p => p.startsWith('v1='))?.split('=')[1];
if (!timestamp || !sig) return false;
const signedPayload = `${timestamp}.${payload}`;
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(WEBHOOK_SECRET),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signatureBuffer = await crypto.subtle.sign(
'HMAC',
key,
new TextEncoder().encode(signedPayload)
);
const expectedSig = Array.from(new Uint8Array(signatureBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
return expectedSig === sig;
}
Deno.serve(async (req: Request) => {
const signature = req.headers.get('stripe-signature') ?? ''
const payload = await req.text()
if (!await verifyStripeWebhook(payload, signature)) {
return new Response('Unauthorized', { status: 401 })
}
const event = JSON.parse(payload)
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
switch (event.type) {
case 'payment_intent.succeeded': {
const { error } = await supabase
.from('orders')
.update({ status: 'paid' })
.eq('stripe_payment_intent_id', event.data.object.id)
if (error) console.error('更新訂單失敗:', error)
break
}
default:
console.log(`未處理的事件類型:${event.type}`)
}
return new Response(JSON.stringify({ received: true }), {
headers: { "Content-Type": "application/json" }
})
})環境變數管理
# 設定 secret(加密儲存)
supabase secrets set MY_API_KEY=sk-...
# 批次設定
supabase secrets set --env-file .env.production
# 查看所有 secrets(只顯示名稱)
supabase secrets list本地開發使用 .env.local:
# .env.local
SUPABASE_URL=http://localhost:54321
SUPABASE_ANON_KEY=your-local-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-local-service-role-key
MY_API_KEY=test-key部署
# 部署單一 function
supabase functions deploy hello-world
# 部署所有 functions
supabase functions deploy
# 不驗證 JWT(公開 API)
supabase functions deploy hello-world --no-verify-jwtCORS 處理
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
Deno.serve(async (req: Request) => {
// 處理 preflight 請求
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
try {
// 你的邏輯
const data = { message: 'Hello' }
return new Response(
JSON.stringify(data),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
} catch (error) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
})Edge Functions 搭配 Supabase 的其他功能(Auth、Database、Storage)可以快速搭建出功能完整的後端服務,而且不需要管理任何伺服器基礎設施,非常適合中小型專案使用。
分享這篇文章