Supabase Edge Functions 实战指南:基于 Deno 边缘运行时的 TypeScript 无服务器函数
Supabase Edge Functions 让你用 TypeScript 在全球边缘节点运行服务端代码,并与平台的数据库、Auth、Storage、Realtime 无缝集成。本文以仓库中的 edge-functions.md 文档为核心骨架,结合 examples/edge-functions 示例工程,完整覆盖函数能力特性、代码编写模式(含 withSupabase 三种鉴权模式)、config.toml 函数级配置、本地开发与部署流程,以及基于 GitHub Actions 的 CI/CD 方案。读完本文,你可以直接按仓库示例搭建、调试并部署自己的 Edge Function。
一、核心定位与技术架构
Edge Functions 是 Supabase 平台的服务端计算能力:函数以 TypeScript 编写,运行在开源 Deno 运行时(V8 引擎)之上,部署在全球边缘节点,代码执行位置靠近终端用户以获得低延迟。它们与平台其他组件的深度集成是主要卖点——函数可以直接访问数据库、Auth、Storage 与 Realtime,而不需要额外的胶水代码。
根据 edge-functions.md 中的技术细节说明,Edge Functions 的关键属性如下:
| 维度 | 说明 |
|---|---|
| 运行时 | Deno(开源,V8 内核) |
| 语言 | TypeScript、JavaScript |
| NPM 支持 | 通过 npm: 导入说明符使用 200 万+ NPM 模块 |
| 扩缩容 | 自动扩缩容,无需手动调优 |
| 安全 | 内置 SSL、防火墙、DDoS 防护 |
| 开源程度 | 本地与生产环境运行同一套 Edge Runtime,无供应商锁定 |
"本地与生产使用同一运行时"这一点在仓库中可以直接印证:官方建议的本地开发流程就是直接用 Supabase CLI 在本地拉起同一套边缘运行时,函数代码无需为生产环境做任何改动。
二、关键特性逐条解读
edge-functions.md 列出了九项关键特性,这里逐项展开:
- 全球部署(Global deployment):函数在全球边缘节点运行,就近服务用户以降低延迟。
- TypeScript 优先:完整类型检查与编辑器自动补全,配合 Deno Language Server 可获得跳转定义、自动补全等 IDE 能力。
- Node.js 兼容:支持 Node.js API,并通过
npm:说明符直接使用海量 NPM 模块。 - 零配置:平台预置了访问所属 Supabase 项目(数据库、Auth、Storage)所需的环境变量,函数内可直接
Deno.env.get()取值。 - 数据库 Webhooks:在表发生 INSERT、UPDATE、DELETE 事件时自动触发函数,是构建数据库事件驱动逻辑的核心机制。
- 区域调用(Regional invocation):可选项地让函数固定运行在靠近数据库的区域,降低数据库访问延迟——适合数据库密集型负载。
- 内建可观测性:实时日志流、可用 SQL 查询的 Log Explorer、以及指标仪表盘。
- CI/CD:通过 Supabase CLI 部署,支持 GitHub Actions 自动化。
- 本地开发体验:热代码重载、Language Server 自动补全,且本地与生产使用相同运行时。
这些特性在示例工程 examples/edge-functions 中都有对应落地,后文会结合具体示例代码逐一验证。
三、函数代码结构:withSupabase 与三种鉴权模式
Edge Function 的入口是导出一个带 fetch 处理函数的默认对象。仓库中的新式示例统一采用 @supabase/server 提供的 withSupabase 包装器,它在请求进入业务逻辑前完成 JWT 校验,并把 Supabase 客户端注入到上下文 ctx 中。withSupabase 的 auth 参数有几种模式,分别对应不同的安全边界:
auth: 'user' —— 以已登录用户身份执行,RLS 生效
select-from-table-with-auth-rls/index.ts 展示了最典型的"用户态"函数:
import { withSupabase } from 'npm:@supabase/server@^1'
export default {
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
try {
// ctx.supabase runs queries as the authenticated user, so RLS applies.
// ctx.userClaims holds the verified user identity.
const { data, error } = await ctx.supabase.from('users').select('*')
if (error) throw error
return Response.json({ user: ctx.userClaims, data })
} catch (error) {
return Response.json({ error: error.message }, { status: 400 })
}
}),
}
要点有三:ctx.supabase 以经过认证的用户身份运行查询,因此 Postgres 的 Row Level Security(RLS)策略自动生效;ctx.userClaims 携带已校验的用户身份声明;调用方必须携带用户 Access Token。文件尾部注释还给出了本地验证用的 curl 命令:
curl -i --request POST 'http://localhost:54321/functions/v1/select-from-table-with-auth-rls' \
--header 'Authorization: Bearer <USER_ACCESS_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{"name":"Functions"}'
auth: 'secret' —— 以密钥保护的服务端端点
openai-image-generation/index.ts 是一个 AI/ML 推理端点的完整实现(正对应特性列表中的 use case),它使用 auth: 'secret' 模式,并演示了函数与 Storage 的集成:
export default {
fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {
const { prompt, imageUrls } = await req.json()
// ... 调用 OpenAI images.edit 生成图片 ...
// ctx.supabaseAdmin 使用 service role,绕过 RLS,
// 可将生成结果上传到私有 Storage bucket
const { error: uploadError } = await ctx.supabaseAdmin.storage
.from('generated-images')
.upload(outputImageName, generatedImageBlob, {
contentType: 'image/png',
upsert: true,
})
// ...
}),
}
这里 ctx.supabaseAdmin 对应 service role 权限的客户端,不受 RLS 约束,适合服务端后台任务(上传生成物、写审计日志等),但端点本身必须用密钥保护,防止匿名调用。
auth: 'none' —— 开放端点,依赖自身签名校验
处理第三方 Webhook 的函数通常关闭 JWT 校验,改由服务商的签名机制保证安全。stripe-webhooks/index.ts 是一个标准实现:
import Stripe from 'npm:stripe@^22'
import { withSupabase } from 'npm:@supabase/server@^1'
const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY') as string)
// This is needed in order to use the Web Crypto API in Deno.
const cryptoProvider = Stripe.createSubtleCryptoProvider()
// Stripe verifies the request via its signature, so deploy with verify_jwt = false.
export default {
fetch: withSupabase({ auth: 'none' }, async (req) => {
const signature = req.headers.get('Stripe-Signature')
// 必须使用 .text() 拿到原始 body,签名校验依赖原始报文而非解析后的 JSON
const body = await req.text()
let receivedEvent
try {
receivedEvent = await stripe.webhooks.constructEventAsync(
body,
signature!,
Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')!,
undefined,
cryptoProvider
)
} catch (err) {
return new Response(err.message, { status: 400 })
}
console.log(`🔔 Event received: ${receivedEvent.id}`)
return Response.json({ ok: true })
}),
}
注释中明确了两条易错点:一是此类函数部署时必须设置 verify_jwt = false(见下节配置);二是 Stripe 签名校验依赖原始请求体,因此必须用 req.text() 而不是 req.json()。同时,Stripe.createSubtleCryptoProvider() 是 Deno 环境下使用 Web Crypto API 的必要初始化。
此外,examples/edge-functions/supabase/functions 目录还包含 Discord/Telegram/Slack bot、OAuth 流程(connect-supabase 演示了完整 OAuth2 + PKCE 授权码流程)、邮件发送(Resend/SMTP)、Upstash Redis 计数与限流、Puppeteer 截图、WASM 模块、单元测试等数十个真实场景的完整示例,可作为对应特性列表中最常见 use case 的参考实现。
四、模块依赖:npm: / jsr: 说明符与 import map
Edge Functions 的依赖来源有两类,从上面示例代码中可以直接看到:
npm:说明符:如import Stripe from 'npm:stripe@^22'、import { withSupabase } from 'npm:@supabase/server@^1',直接引用 NPM 模块并支持版本范围;jsr:说明符:如 openai-image-generation 中的import OpenAI from 'jsr:@openai/openai@^6',引用 JSR 注册表。
对于 URL 导入(https://deno.land/x/...、https://esm.sh/...),项目可以通过 import map 集中管理版本。examples/edge-functions/supabase/functions/import_map.json 就是一份真实配置:
{
"imports": {
"oak": "https://deno.land/x/oak@v11.1.0/mod.ts",
"openai": "https://esm.sh/openai@3.1.0",
"stripe": "https://esm.sh/stripe@11.1.0?target=deno",
"react": "https://esm.sh/react@18.2.0",
"kysely": "https://esm.sh/kysely@0.23.4",
"upstash_redis": "https://deno.land/x/upstash_redis@v1.19.3/mod.ts"
}
}
通过 import map,业务代码只需 import { Application } from 'oak',具体版本与 CDN 源由 map 文件锁定,避免每个函数文件重复写完整 URL。
五、函数级配置:config.toml 中的 [functions.<name>]
每个函数的行为在 supabase/config.toml 的 [functions.<函数名>] 小节中配置。examples/edge-functions/supabase/config.toml 给出了最完整的参考,覆盖三种配置项:
# 1. 关闭 JWT 校验(Webhook、Bot 回调等外部触发场景)
[functions.stripe-webhooks]
verify_jwt = false
[functions.discord-bot] # 未显式声明时默认为 verify_jwt = true
# 2. 为特定函数指定 import map
[functions.kysely-postgres]
verify_jwt = true
import_map = "./functions/import_map.json"
# 3. 自定义入口文件与打包静态资源
[functions.simple-mcp-server]
verify_jwt = false
entrypoint = "./functions/mcp/simple-mcp-server/index.ts"
[functions.wasm-modules]
verify_jwt = true
static_files = ["./functions/wasm-modules/add-wasm/pkg/*.wasm"]
参数说明:
| 参数 | 默认值 | 作用 |
|---|---|---|
verify_jwt |
true |
是否校验请求中的 JWT。第三方 Webhook(Stripe、GitHub、Slack 等)必须置为 false,改由服务方签名机制保证安全 |
import_map |
无 | 为该函数指定 import map 文件路径 |
entrypoint |
./functions/<name>/index.ts |
自定义函数入口文件 |
static_files |
无 | 将本地静态文件(如 .wasm 产物)打包进部署 |
值得注意的是,examples/edge-functions/supabase/config.toml 中 40 余个函数只有约一半显式写了 verify_jwt——其余空小节表示沿用默认值(校验开启)。本仓库自身的 supabase/config.toml 中也为 search-embeddings 函数配置了 verify_jwt = false,说明即使是站点自身使用的内部函数也遵循同一套配置规范。
六、本地开发:与生产同一运行时
examples/edge-functions/README.md 给出的本地开发流程如下:
- 确保 Docker 守护进程在运行,执行
supabase start启动完整本地栈; cp ./supabase/.env.local.example ./supabase/.env.local生成本地环境文件,并为对应函数填入所需变量(如OPENAI_API_KEY、STRIPE_WEBHOOK_SIGNING_SECRET);- 启动函数服务:
supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt
其中 --env-file 指定环境变量来源,--no-verify-jwt 在本地调试时跳过 JWT 校验(生产环境则以 config.toml 中 verify_jwt 为准)。函数默认在 http://localhost:54321/functions/v1/<函数名> 上提供访问。
示例仓库还附带了一个 create-react-app 测试客户端(examples/edge-functions/app 目录,可用 cd app && npm install && npm start 启动),相当于一个"类 Postman"的调用面板,既可打本地函数也可打已部署函数。本地开发期间支持热重载,编辑器配合 Deno Language Server 即可获得自动补全,且由于本地与生产使用同一 Edge Runtime,行为差异被降到最低——这正是原文档"no vendor lock-in"主张的工程含义。
七、部署与 CI/CD
手动部署流程
按 examples/edge-functions/README.md 的顺序:
# 1. 登录 CLI(先在 Dashboard 生成 Access Token,再执行)
supabase login
# 2. 关联远程项目
supabase link --project-ref your-project-ref
# 3. 上传密钥(推荐为生产环境单独准备 .env 文件)
supabase secrets set --env-file ./supabase/.env.local
supabase secrets list # 验证,并查看平台默认注入的变量
# 4. 部署函数
supabase functions deploy your-function-name
自 Supabase CLI v1.62.0 起,supabase functions deploy 不带函数名即可一次性部署全部函数,适合多函数项目。
GitHub Actions 自动部署
原文档"CI/CD"特性对应的仓库落地是一个完整的 Actions 工作流(示例见 examples/edge-functions/README.md 及 github-action-deploy 函数):
name: Deploy Function
on:
push:
branches:
- main
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
PROJECT_ID: your-project-id
steps:
- uses: actions/checkout@v3
- uses: supabase/setup-cli@v1
with:
version: latest
- run: supabase functions deploy --project-ref $PROJECT_ID
要点:SUPABASE_ACCESS_TOKEN 存为仓库 Secret;setup-cli Action 负责安装指定版本的 Supabase CLI;推送到 main 或手动触发(workflow_dispatch)即完成部署。
八、常见使用场景与仓库示例映射
edge-functions.md 列出的六类常见场景,在示例工程中都能找到对应的可运行实现:
| 使用场景 | 仓库对应示例 |
|---|---|
| Webhook 处理(Stripe、GitHub、Twilio) | stripe-webhooks、cloudflare-turnstile |
| 服务端 API 路由与中间件 | oak-server(基于 Oak 框架的完整路由应用)、restful-tasks |
| 定时任务 | 通过 pg_cron + 数据库 Webhooks 触发函数(特性列表中的标准做法) |
| AI/ML 推理端点 | openai-image-generation、huggingface-image-captioning、elevenlabs-text-to-speech |
| 邮件与通知 | send-email-resend、send-email-smtp、auth-hook-react-email-resend |
| 第三方 API 集成 | discord-bot、telegram-bot、slack-bot-mention |
另外两类仓库中同样有现成参考:外部 OAuth 应用授权(connect-supabase 完整实现 PKCE 授权码流程并调用 Management API),以及性能敏感型直连 Postgres 的方案(postgres-on-the-edge、drizzle、kysely-postgres)。
九、小结
Supabase Edge Functions 的核心价值可以归纳为三点:其一,Deno 边缘运行时带来低延迟的全球执行,且本地与生产同构,开发即调试、调试即生产;其二,withSupabase 的 user / secret / none 三种鉴权模式与预置环境变量,让函数可以以正确的安全边界复用数据库、Auth、Storage 全部平台能力,RLS 在用户态查询中自动生效;其三,config.toml 的函数级配置(verify_jwt、import_map、entrypoint、static_files)加上 CLI 部署与 GitHub Actions 工作流,构成了一条从本地热重载到持续交付的完整链路。建议以 examples/edge-functions 为起点,先跑通一个 auth: 'user' 的表查询函数,再逐步尝试 Webhook 与 AI 推理两类典型负载。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00