mcpField
S
supabase-edge-functions

supabase-edge-functions

github.com/nice-wolf-studio云与 DevOps19358 次安装53 周前更新
代码仓库

关于 supabase-edge-functions

supabase-edge-functions 是供开发者构建和运维 Supabase Edge Functions 的技能,涵盖调用、本地开发、部署、日志、测试、认证及常见运维模式。

主要功能

  1. 01通过 HTTP 请求调用函数
  2. 02使用 Supabase CLI 创建和部署函数
  3. 03在 Supabase 本地运行函数
  4. 04配置密钥与认证
  5. 05测试函数并查看日志
  6. 06使用重试和批量调用

适用场景

  1. 01携带请求数据或用户令牌调用函数
  2. 02在本地开发和测试函数
  3. 03部署函数并检查运行日志
  4. 04自动执行重复或重试调用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nice-wolf-studio/claude-code-supabase-skills --skill supabase-edge-functions

安装后在 Claude Code 中输入/supabase-edge-functions确认已加载。

详细介绍

Supabase Edge Functions

概述

此技能提供用于操作 Supabase Edge Functions 的方法——这些是运行在 Deno Deploy 上的无服务器 TypeScript/JavaScript 函数。可用于调用函数、部署代码和管理函数生命周期。

前提条件

必需的环境变量:

export SUPABASE_URL="https://your-project.supabase.co"
export SUPABASE_KEY="your-anon-or-service-role-key"

必需的工具:

  • Supabase CLI(supabase 命令)
  • Deno(用于本地开发)

安装 Supabase CLI:

# macOS
brew install supabase/tap/supabase

# Linux
curl -fsSL https://github.com/supabase/cli/releases/latest/download/supabase_linux_amd64.tar.gz | tar -xz
sudo mv supabase /usr/local/bin/

# Windows (PowerShell)
scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
scoop install supabase

辅助脚本: 此技能使用共享的 Supabase API 辅助工具来调用函数:

source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

调用 Edge Functions

基本调用

使用 POST 调用函数:

source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

FUNCTION_NAME="hello-world"

supabase_post "/functions/v1/${FUNCTION_NAME}" '{
  "name": "Alice"
}'

使用 GET 调用:

source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

FUNCTION_NAME="get-data"

supabase_get "/functions/v1/${FUNCTION_NAME}?id=123"
使用标头调用

传递自定义标头:

FUNCTION_NAME="authenticated-function"
USER_TOKEN="user-access-token"

curl -s -X POST \
    "${SUPABASE_URL}/functions/v1/${FUNCTION_NAME}" \
    -H "apikey: ${SUPABASE_KEY}" \
    -H "Authorization: Bearer ${USER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{"action": "process"}'
使用身份验证调用

以已通过身份验证的用户身份调用函数:

source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

FUNCTION_NAME="user-profile"
ACCESS_TOKEN="user-jwt-token"

curl -s -X POST \
    "${SUPABASE_URL}/functions/v1/${FUNCTION_NAME}" \
    -H "apikey: ${SUPABASE_KEY}" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{}'

函数管理(CLI)

初始化函数

创建新的 Edge Function:

# Navigate to your Supabase project directory
cd /path/to/project

# Create new function
supabase functions new my-function

# This creates: supabase/functions/my-function/index.ts
函数模板

基本函数结构:

// supabase/functions/my-function/index.ts

import { serve } from "https://deno.land/[email protected]/http/server.ts"

serve(async (req) => {
  const { name } = await req.json()

  const data = {
    message: `Hello ${name}!`,
  }

  return new Response(
    JSON.stringify(data),
    { headers: { "Content-Type": "application/json" } },
  )
})

带身份验证的函数:

import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  // Get JWT from Authorization header
  const authHeader = req.headers.get('Authorization')!
  const token = authHeader.replace('Bearer ', '')

  // Create Supabase client with user's token
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    { global: { headers: { Authorization: authHeader } } }
  )

  // Get authenticated user
  const { data: { user }, error } = await supabase.auth.getUser(token)

  if (error || !user) {
    return new Response('Unauthorized', { status: 401 })
  }

  return new Response(
    JSON.stringify({ message: `Hello ${user.email}!` }),
    { headers: { "Content-Type": "application/json" } },
  )
})
部署函数

将函数部署到 Supabase:

# Login to Supabase (first time only)
supabase login

# Link to your project (first time only)
supabase link --project-ref your-project-ref

# Deploy specific function
supabase functions deploy my-function

# Deploy with custom environment variables
supabase functions deploy my-function \
  --env-file ./supabase/.env.local

# Deploy all functions
supabase functions deploy
设置环境变量

为 Edge Functions 设置密钥:

# Set individual secret
supabase secrets set MY_SECRET_KEY=value123

# Set multiple secrets from file
# Create .env file:
# API_KEY=abc123
# DATABASE_URL=postgres://...

supabase secrets set --env-file .env

# List secrets (names only, not values)
supabase secrets list

# Unset secret
supabase secrets unset MY_SECRET_KEY
本地开发

在本地运行函数:

# Start local Supabase (includes edge functions)
supabase start

# Serve functions locally
supabase functions serve

# Serve specific function
supabase functions serve my-function --env-file ./supabase/.env.local

# Invoke local function
curl http://localhost:54321/functions/v1/my-function \
  -H "Authorization: Bearer ${SUPABASE_KEY}" \
  -d '{"name": "test"}'
删除函数

移除已部署的函数:

# Delete function from Supabase dashboard or using SQL
# Note: No direct CLI command to delete, must use dashboard

# Remove local function file
rm -rf supabase/functions/my-function

常见模式

调用并处理响应
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

FUNCTION_NAME="process-data"

response=$(supabase_post "/functions/v1/${FUNCTION_NAME}" '{
  "action": "calculate",
  "values": [1, 2, 3, 4, 5]
}')

if [[ $? -eq 0 ]]; then
    result=$(echo "$response" | jq -r '.result')
    echo "Function result: $result"
else
    echo "Function invocation failed"
    exit 1
fi
批量调用函数
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

FUNCTION_NAME="send-email"
RECIPIENTS=("[email protected]" "[email protected]" "[email protected]")

for email in "${RECIPIENTS[@]}"; do
    echo "Processing $email..."

    supabase_post "/functions/v1/${FUNCTION_NAME}" '{
      "to": "'"$email"'",
      "subject": "Hello",
      "body": "Test message"
    }'

    echo "✓ Sent to $email"
done
带重试逻辑的函数
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"

invoke_with_retry() {
    local function_name="$1"
    local payload="$2"
    local max_retries=3
    local retry_count=0

    while [[ $retry_count -lt $max_retries ]]; do
        if response=$(supabase_post "/functions/v1/${function_name}" "$payload" 2>&1); then
            echo "$response"
            return 0
        else
            retry_count=$((retry_count + 1))
            echo "Retry $retry_count/$max_retries..." >&2
            sleep 2
        fi
    done

    echo "Function failed after $max_retries retries" >&2
    return 1
}

# Use it
invoke_with_retry "my-function" '{"action": "process"}'
函数部署脚本
#!/bin/bash
# deploy-function.sh

FUNCTION_NAME="${1:-my-function}"

echo "Deploying function: $FUNCTION_NAME"

# Validate function exists
if [[ ! -d "supabase/functions/$FUNCTION_NAME" ]]; then
    echo "Error: Function $FUNCTION_NAME not found"
    exit 1
fi

# Deploy
if supabase functions deploy "$FUNCTION_NAME"; then
    echo "✓ Deployed successfully"

    # Test invocation
    echo "Testing function..."
    response=$(curl -s -X POST \
        "${SUPABASE_URL}/functions/v1/${FUNCTION_NAME}" \
        -H "apikey: ${SUPABASE_KEY}" \
        -H "Content-Type: application/json" \
        -d '{}')

    echo "Test response: $response"
else
    echo "✗ Deployment failed"
    exit 1
fi
监控函数日志
# View function logs (requires Supabase CLI)
supabase functions logs my-function

# Follow logs in real-time
supabase functions logs my-function --follow

# Filter logs by level
supabase functions logs my-function --level error

# View logs from specific time
supabase functions logs my-function --since 1h

高级模式

可访问数据库的函数
// supabase/functions/get-user-data/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
  )

  const { userId } = await req.json()

  const { data, error } = await supabase
    .from('users')
    .select('*')
    .eq('id', userId)
    .single()

  if (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 400,
      headers: { 'Content-Type': 'application/json' }
    })
  }

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' }
  })
})
调用外部 API 的函数
// supabase/functions/fetch-weather/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts"

serve(async (req) => {
  const { city } = await req.json()
  const apiKey = Deno.env.get('WEATHER_API_KEY')

  const response = await fetch(
    `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`
  )

  const data = await response.json()

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' }
  })
})
定时函数(Cron)
// supabase/functions/daily-cleanup/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  // Verify request is from Supabase Cron
  const authHeader = req.headers.get('Authorization')
  if (authHeader !== `Bearer ${Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')}`) {
    return new Response('Unauthorized', { status: 401 })
  }

  const supabase = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
  )

  // Delete old records
  const { data, error } = await supabase
    .from('logs')
    .delete()
    .lt('created_at', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString())

  return new Response(JSON.stringify({ deleted: data?.length ?? 0 }), {
    headers: { 'Content-Type': 'application/json' }
  })
})

在 Supabase Dashboard 中设置 Cron 作业:

-- In SQL Editor, create pg_cron job:
select cron.schedule(
  'daily-cleanup',
  '0 2 * * *', -- Run at 2 AM daily
  $$
  select
    net.http_post(
      url := 'https://your-project.supabase.co/functions/v1/daily-cleanup',
      headers := '{"Content-Type": "application/json", "Authorization": "Bearer YOUR_SERVICE_ROLE_KEY"}'::jsonb,
      body := '{}'::jsonb
    ) as request_id;
  $$
);

测试函数

本地测试
# Start local environment
supabase start

# Serve function
supabase functions serve my-function

# Test with curl
curl http://localhost:54321/functions/v1/my-function \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -d '{"test": "data"}'
集成测试脚本
#!/bin/bash
# test-function.sh

FUNCTION_NAME="$1"
TEST_CASES_FILE="$2"

if [[ ! -f "$TEST_CASES_FILE" ]]; then
    echo "Test cases file not found"
    exit 1
fi

echo "Testing function: $FUNCTION_NAME"

while IFS= read -r test_case; do
    echo "Test case: $test_case"

    response=$(curl -s -X POST \
        "${SUPABASE_URL}/functions/v1/${FUNCTION_NAME}" \
        -H "apikey: ${SUPABASE_KEY}" \
        -H "Content-Type: application/json" \
        -d "$test_case")

    echo "Response: $response"
    echo "---"
done < "$TEST_CASES_FILE"

错误处理

函数错误会返回 HTTP 状态码:

状态含义
200成功
400错误请求(输入无效)
401未授权(身份验证无效或缺失)
403禁止访问(权限不足)
500内部服务器错误(函数崩溃)
504网关超时(函数运行时间过长)

超时限制: Edge Functions 的 CPU 时间限制为 2 秒,墙钟超时为 150 秒。

安全最佳实践

  1. 验证输入:始终验证并清理请求数据
  2. 谨慎使用 service role key:仅用于管理函数,切勿暴露给客户端
  3. 实施身份验证:检查受保护函数的用户令牌
  4. 速率限制:为公开函数实施速率限制
  5. 环境变量:将密钥存储在 Supabase secrets 中,而不是代码里
  6. CORS:适当配置 CORS 标头
  7. 错误消息:不要在错误响应中泄露敏感信息

性能提示

  1. 冷启动:函数可能会发生冷启动(延迟 100-200ms)
  2. 保持函数精简:冷启动更快,也更易调试
  3. 缓存外部数据:对重复的 API 调用使用内存缓存
  4. 并行执行:使用 Promise.all() 执行并发操作
  5. 流式传输大型响应:大型数据传输使用流式处理

API 文档

评论

支持 Markdown