Supabase Edge Functions本地开发 #
一、本地环境配置 #
1.1 安装依赖 #
bash
# 确保已安装Supabase CLI
supabase --version
# 登录
supabase login
1.2 初始化项目 #
bash
# 初始化
supabase init
# 链接远程项目
supabase link --project-ref your-project-ref
1.3 启动本地服务 #
bash
# 启动所有服务
supabase start
# 输出
API URL: http://localhost:54321
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio URL: http://localhost:54323
Inbucket URL: http://localhost:54324
anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
service_role key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
二、函数开发 #
2.1 创建函数 #
bash
supabase functions new my-function
2.2 目录结构 #
text
supabase/
├── functions/
│ ├── my-function/
│ │ └── index.ts
│ ├── another-function/
│ │ └── index.ts
│ └── _shared/
│ ├── supabase.ts
│ └── utils.ts
├── migrations/
├── config.toml
└── .env
2.3 共享代码 #
typescript
// functions/_shared/supabase.ts
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
export function createSupabaseClient(authToken?: string) {
return createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
{
global: authToken ? {
headers: { Authorization: `Bearer ${authToken}` },
} : undefined,
}
)
}
typescript
// functions/my-function/index.ts
import { createSupabaseClient } from '../_shared/supabase.ts'
Deno.serve(async (req) => {
const authHeader = req.headers.get('Authorization')
const token = authHeader?.replace('Bearer ', '')
const supabase = createSupabaseClient(token)
// 使用supabase客户端...
})
三、本地测试 #
3.1 启动函数服务 #
bash
# 启动函数服务
supabase functions serve
# 指定环境变量文件
supabase functions serve --env-file .env.local
3.2 测试请求 #
bash
# GET请求
curl http://localhost:54321/functions/v1/my-function \
-H "Authorization: Bearer YOUR_ANON_KEY"
# POST请求
curl -X POST http://localhost:54321/functions/v1/my-function \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "test"}'
# 带自定义头
curl http://localhost:54321/functions/v1/my-function \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "X-Custom-Header: value"
3.3 测试脚本 #
typescript
// test.ts
const SUPABASE_URL = 'http://localhost:54321'
const ANON_KEY = 'your-anon-key'
async function testFunction() {
const response = await fetch(`${SUPABASE_URL}/functions/v1/my-function`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${ANON_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Test' }),
})
const data = await response.json()
console.log('Response:', data)
}
testFunction()
bash
# 运行测试
deno run --allow-net test.ts
四、环境变量 #
4.1 本地环境变量 #
bash
# supabase/.env
SUPABASE_URL=http://localhost:54321
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
MY_API_KEY=your-api-key
4.2 使用环境变量 #
typescript
Deno.serve(async (req) => {
const apiKey = Deno.env.get('MY_API_KEY')
if (!apiKey) {
console.error('MY_API_KEY not set')
return new Response('Configuration error', { status: 500 })
}
// 使用apiKey...
})
4.3 不同环境配置 #
bash
# 开发环境
.env.development
# 生产环境
.env.production
# 使用指定环境
supabase functions serve --env-file .env.development
五、调试技巧 #
5.1 控制台日志 #
typescript
Deno.serve(async (req) => {
console.log('=== Request Start ===')
console.log('Time:', new Date().toISOString())
console.log('Method:', req.method)
console.log('URL:', req.url)
const body = await req.text()
console.log('Body:', body)
try {
const json = JSON.parse(body)
console.log('Parsed JSON:', json)
} catch (e) {
console.log('Not JSON body')
}
// 处理逻辑...
console.log('=== Request End ===')
return new Response('OK')
})
5.2 错误追踪 #
typescript
Deno.serve(async (req) => {
try {
// 处理逻辑
const result = await riskyOperation()
return new Response(JSON.stringify(result))
} catch (error) {
console.error('Error occurred:', error)
console.error('Stack:', error.stack)
return new Response(
JSON.stringify({
error: error.message,
stack: Deno.env.get('ENVIRONMENT') === 'development' ? error.stack : undefined,
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
)
}
})
5.3 性能监控 #
typescript
Deno.serve(async (req) => {
const startTime = performance.now()
// 处理逻辑
const result = await processRequest()
const endTime = performance.now()
const duration = endTime - startTime
console.log(`Request took ${duration.toFixed(2)}ms`)
return new Response(JSON.stringify(result))
})
六、VS Code配置 #
6.1 Deno扩展 #
text
安装扩展
├── Deno (denoland.vscode-deno)
└── 配置Deno为默认TypeScript服务器
6.2 settings.json #
json
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"deno.importMap": "./supabase/functions/import_map.json"
}
6.3 import_map.json #
json
{
"imports": {
"@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2",
"std/": "https://deno.land/std@0.208.0/"
}
}
七、常见问题 #
7.1 端口冲突 #
bash
# 检查端口占用
lsof -i :54321
# 修改端口
supabase functions serve --port 54322
7.2 热重载不生效 #
bash
# 重启服务
supabase functions serve --no-verify-jwt
7.3 环境变量不加载 #
bash
# 确保在正确目录
cd supabase
# 显式指定环境文件
supabase functions serve --env-file .env
八、最佳实践 #
8.1 代码组织 #
typescript
// 分离逻辑
// functions/_shared/handlers.ts
export async function handleGet(req: Request) {
// GET处理逻辑
}
export async function handlePost(req: Request) {
// POST处理逻辑
}
typescript
// functions/my-function/index.ts
import { handleGet, handlePost } from '../_shared/handlers.ts'
Deno.serve(async (req) => {
switch (req.method) {
case 'GET':
return handleGet(req)
case 'POST':
return handlePost(req)
default:
return new Response('Method not allowed', { status: 405 })
}
})
8.2 类型定义 #
typescript
// functions/_shared/types.ts
export interface User {
id: string
email: string
name: string
}
export interface ApiResponse<T> {
success: boolean
data?: T
error?: string
}
九、总结 #
本地开发要点:
| 操作 | 命令 |
|---|---|
| 启动服务 | supabase functions serve |
| 测试请求 | curl localhost:54321/functions/v1/name |
| 查看日志 | 控制台输出 |
| 环境变量 | –env-file .env |
下一步,让我们学习函数调用!
最后更新:2026-03-28