Supabase Edge Functions 实战指南:本地开发、测试客户端与多环境部署
Supabase Edge Functions 是构建在 Deno 运行时之上的无服务器函数能力,开发者用 TypeScript 编写业务逻辑,通过 Supabase CLI 一键完成本地调试与云端部署。本指南以 examples/edge-functions 示例仓库为依托,完整讲解从环境准备、本地起服务、密钥配置、浏览器测试客户端,到 CLI 手动部署与 GitHub Actions 自动部署的全链路流程,同时结合仓库内数十个真实函数源码与 supabase/config.toml 配置,帮助读者掌握 Edge Functions 的工程化落地方法。
Edge Functions 与示例仓库概览
Supabase Edge Functions 的核心特征在于:
- 语言与运行时:函数使用 TypeScript 编写,运行在 Deno 运行时之上,天然支持直接 import URL 依赖与
npm:/jsr:作用域包。 - 部署方式:通过 Supabase CLI 完成部署与密钥管理,无需自建服务器。
- HTTP 入口:每个函数导出
fetch处理器,符合 Deno Deploy 的标准形态。
在示例仓库中,所有函数均存放于 supabase/functions/ 目录(每个子目录名即函数名,例如 browser-with-cors),旁边配套的 import_map.json 集中声明了 oak、openai、stripe、grammy、@supabase/supabase-js、postgres、puppeteer、kysely、react 等第三方依赖映射,供需要 import map 的函数引用。仓库根目录的 supabase/config.toml 则负责描述整个本地项目与每个函数的部署配置。
示例函数全景:按场景分类的代码素材库
示例目录中沉淀了大量可直接借鉴的真实函数,从源码结构看可以大致划分为以下几类,每一类都包含完整可运行的 index.ts 入口文件:
| 场景 | 代表函数 | 关键依赖 / 能力 |
|---|---|---|
| 鉴权与安全 | browser-with-cors、custom-jwt-validation、select-from-table-with-auth-rls | 基于用户的认证、CORS、RLS 穿透查询 |
| 邮件与通知 | send-email-resend、send-email-smtp、auth-hook-react-email-resend | Resend、SMTP、React Email 模板 |
| 数据库直连 | postgres-on-the-edge、kysely-postgres、drizzle | Postgres 连接池、Kysely/Drizzle ORM |
| AI / 大模型 | openai、openai-image-generation、huggingface-image-captioning、elevenlabs-text-to-speech | 文本补全、图像生成、图像理解、语音合成 |
| 图像处理与 OG 图 | image-manipulation、opengraph、og-image-with-storage-cdn、tweet-to-image | Canvas 渲染、og_edge、Storage CDN |
| 第三方集成 | telegram-bot、discord-bot、slack-bot-mention、stripe-webhooks、cloudflare-turnstile | 各平台 Bot/Webhook/验证码 |
| 基础设施与限流 | upstash-redis-counter、upstash-redis-ratelimit、streams | Redis 计数与速率限制、流式响应 |
| 可观测性 | sentry、sentryfied | Sentry 错误上报 |
| 工程化进阶 | unit-testing、mcp、wasm-modules | Deno 单元测试、MCP Server、WASM 调用 |
| 位置 / 基础工具 | location、restful-tasks、oak-server | 请求头解析、RESTful 任务、Oak Web 框架 |
这些函数多数配有独立的 README,团队持续更新维护,可作为日常开发时随取随用的代码素材库。
本地开发:从零启动整套本地环境
本地开发的前提是机器上已安装 Docker(守护进程保持运行),并已下载或升级到最新版 Supabase CLI。整体流程如下:
第 1 步:启动本地堆栈
在示例仓库根目录执行:
supabase start
该命令会依据 supabase/config.toml 拉起本地 Postgres(示例中数据库主版本为 15)、API 网关(端口 54321)、本地鉴权等依赖。仓库的 config.toml 中同时定义了三个存储桶 my-bucket、videos 与公开可读的 images(objects_path 指向 supabase/buckets/images),供文件上传类函数(如 background-upload-storage)演示使用。
第 2 步:准备本地环境变量文件
仓库提供了本地密钥模板 supabase/.env.local.example,复制为可编辑文件:
cp ./supabase/.env.local.example ./supabase/.env.local
第 3 步:按需填写密钥
.env.local 中以注释分区的方式列出了每个函数所需的密钥,例如:
RESEND_API_KEY、SEND_EMAIL_HOOK_SECRET:供 auth-hook-react-email-resend 与 send-email-resend 使用;OPENAI_API_KEY:供 openai 系列使用;DB_HOSTNAME、DB_PASSWORD、DB_USER、DB_SSL_CERT:供 postgres-on-the-edge 与 kysely-postgres 直连远端数据库使用(模板注释提醒在 Dashboard 的数据库设置页开启连接池并选择 Transaction 模式);IPINFO_TOKEN:供 location 解析客户端 IP 地理位置使用;STRIPE_API_KEY、STRIPE_WEBHOOK_SIGNING_SECRET:供 stripe-webhooks 验签与查询使用;TELEGRAM_BOT_TOKEN、FUNCTION_SECRET:供 telegram-bot 使用;UPSTASH_REDIS_REST_URL、UPSTASH_REDIS_REST_TOKEN:供两个 Upstash Redis 示例使用;SMTP_HOSTNAME、SMTP_PORT等:供 send-email-smtp 使用。
只需填写你要运行的那个函数对应的变量,其余保持占位即可。
第 4 步:本地托管函数服务
在仓库根目录执行:
supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt
--env-file 把本地密钥注入函数运行时(对应 Deno.env.get(...) 读取),--no-verify-jwt 表示本地调试阶段跳过 JWT 校验,方便用 curl 快速验证。此时函数会暴露在 http://localhost:54321/functions/v1/<function-name>。
第 5 步:发起测试请求
三种方式任选其一:
- 使用函数源码注释中的 CURL 示例。例如 browser-with-cors 源码末尾给出了带用户令牌的调用方式:
curl -i --location --request POST 'http://localhost:54321/functions/v1/browser-with-cors' \
--header 'Authorization: Bearer <USER_ACCESS_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{"name":"Functions"}'
该函数在 config.toml 中被声明为 verify_jwt = true,因此生产调用必须携带有效 JWT;select-from-table-with-auth-rls 同属此类——它通过 ctx.supabase 以当前登录用户身份查询 users 表,从而让行级安全(RLS)规则在函数内继续生效。
- 使用 supabase-js 客户端的
invoke方法(见下文"从客户端调用"章节)。 - 使用随仓库附带的浏览器测试客户端 app 进行可视化请求。
从源码理解函数骨架:认证与 CORS 的正确姿势
示例函数大量采用 npm:@supabase/server 的 withSupabase 包装器来统一处理鉴权与跨域。以 select-from-table-with-auth-rls 为例,其结构如下:
import { withSupabase } from 'npm:@supabase/server@^1'
console.log(`Function "select-from-table-with-auth-rls" up and running!`)
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 })
}
}),
}
其中值得注意的工程要点:
auth: 'user'声明该端点需要登录态,即部署时应开启verify_jwt = true,与 supabase/config.toml 中该函数条目保持一致;而 browser-with-cors 的注释也明确写着"Authenticated endpoint, so deploy with verify_jwt = true",可见 JWT 开关是函数设计与部署配置必须对齐的一环。withSupabase会自动处理 CORS 头,这解释了为什么需要浏览器直调(带 Cookie/跨域)的函数只需一个包装器即可。仓库同时在 supabase/functions/_shared/ 提供了 cors.ts 手写 CORS 实现作为低层替代方案,供不使用包装器的自定义函数复制使用。- 自定义 JWT 校验:若后端 Token 不是 Supabase 默认签发格式(例如接入 Clerk 等第三方 IdP),supabase/functions/_shared/jwt/ 目录下的
default.ts、clerk.ts、legacy-jwt.ts提供了多种解析策略,custom-jwt-validation 演示了如何对接。 - 本地 Deno 语言服务:多个函数源码顶部注释建议开发者按照 Deno 官方指引配置编辑器语言服务,以获得自动补全与跳转定义能力。
浏览器测试客户端:一套模拟 Postman 的 React 界面
示例仓库在 app 目录内置了一个基于 Create React App(见 app/package.json,使用 Tailwind 与 @supabase/supabase-js)构建的测试客户端,可同时用于本地与线上函数的请求测试。
本地测试
cd app
npm install
npm start
启动后打开本地页面即可测试。需要注意:本地模式下顶部下拉框不生效,无论选择哪个函数,invoke 只会调用 CLI 当前正在 serve 的那个函数。这个下拉列表定义在 app/src/functionsList.js 中,首项 'local: Whatever function is currently served by the CLI' 正是对这一行为的说明。
在页面逻辑 app/src/App.js 中可以看到,界面通过 supabase.functions.invoke(supaFunction, { body }) 发起请求,并在请求区支持 JSON 编辑器输入(默认请求体为 { name: 'world' })。由于部分示例(如 browser-with-cors)要求登录态,界面还内置了基于 supabase.auth.signInWithPassword / signUp 的注册登录表单,确保可以拿到真实用户令牌去调用受保护函数。
测试已部署的函数
切换到线上测试前,需要把客户端指向云端项目:
- 在 app 目录内依据模板创建
.env文件并填入项目 API 配置(URL 与默认 publishable key 来自 Supabase Dashboard 的 API Settings 页面); - 执行
npm install与npm start启动。
sapp/src/utils/supabaseClient.js 展示了客户端的双环境适配方式:通过 REACT_APP_SUPABASE_URL 与 REACT_APP_SUPABASE_DEFAULT_PUBLISHABLE_KEY 两个环境变量覆盖默认的 http://localhost:54321 与本地方案的匿名 key。这意味着同一套界面既可以在本地直连 CLI,也可以在配置后访问云端函数,本地/线上切换只靠环境变量驱动。
部署到云端:CLI 完整操作链
当函数在本地验证通过后,即可通过 CLI 部署到云端项目。完整流程分为四步:
第 1 步:生成访问令牌并登录
在 Supabase Dashboard 的 Account Tokens 页面点击 "Generate New Token",复制新生成的令牌后执行:
supabase login
按提示粘贴令牌完成登录。
第 2 步:关联云端项目
在仓库根目录执行(将 your-project-ref 替换为真实项目引用 ID):
supabase link --project-ref your-project-ref
第 3 步:设置生产密钥
supabase secrets set --env-file ./supabase/.env.local
该命令把本地 .env.local 中的密钥批量写入云端。README 同时强调:上述做法隐含了一个前提——本地密钥与生产密钥相同;更推荐的做法是为生产单独维护一份 .env 文件,部署时用生产专用文件设置环境变量,避免把本地调试密钥带上生产。上传后可用以下命令核对生效情况并查看 CLI 默认注入的其它环境变量:
supabase secrets list
第 4 步:部署函数
在仓库根目录执行:
supabase functions deploy your-function-name
部署完成后,记得回到前端测试客户端,从 .env 中移除本地专用的 SUPA_FUNCTION_LOCALHOST 变量并重启应用,让请求真正指向云端函数地址。
函数级部署配置:verify_jwt 与 import map
函数的行为可以在 supabase/config.toml 中按函数名逐一覆盖。最常用的是 JWT 校验开关,例如让某个 Webhook/回调类函数不要求令牌:
[functions.hello-world]
verify_jwt = false
在真实仓库的 config.toml 中可以观察到两条清晰的配置纪律:
- 凡是需要登录态的业务端点一律开校验:
browser-with-cors、elevenlabs-text-to-speech、image-manipulation、location、openai、puppeteer、upstash-redis-counter、upstash-redis-ratelimit等均设置为verify_jwt = true; - Webhook、公开入口与 Bot 类函数关闭校验:
stripe-webhooks、telegram-bot、discord-bot、slack-bot-mention、send-email-resend、cloudflare-turnstile、og-image-with-storage-cdn、get-tshirt-competition等均设置为verify_jwt = false(签名验证由函数内部自行完成,例如 Stripe Webhook 用签名密钥、Telegram/Discord 用 Bot 令牌或公钥验签)。
除 verify_jwt 外,config.toml 还能配置 import map 与资源文件,仓库中已有三处真实用法:
[functions.kysely-postgres]
verify_jwt = true
import_map = "./functions/import_map.json"
[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"]
import_map为函数指定依赖映射文件(本仓库统一使用 supabase/functions/import_map.json);entrypoint在函数目录内存在多个可执行文件时显式指定入口;static_files用于随函数分发静态资源(此处把 Rust 编译产出的.wasm文件一并部署,支撑 wasm-modules 的 WebAssembly 调用演示)。
GitHub Actions 自动部署:推送即上线
对于需要持续迭代的函数,示例仓库提供了可开箱即用的 CI/CD 方案:每当代码推送到或合并进 main 分支(也可手动触发 workflow_dispatch)时,自动部署全部 Edge Functions。工作流文件位于 .github/workflows/deploy.yaml,其核心骨架如下:
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
仓库内的 deploy.yaml 与上例略有演进:项目引用 ID 同样放入仓库 Secrets(SUPABASE_PROJECT_ID),checkout 与 setup-cli 两个 Action 均固定到具体 commit 版本以保证可复现,同时为 Job 声明了最小 permissions: contents: read 权限。整体思路一致,你需要做的准备是:
- 在仓库的 Actions Secrets 中配置
SUPABASE_ACCESS_TOKEN(即登录用的个人访问令牌); - 配置
PROJECT_ID或SUPABASE_PROJECT_ID为你的项目引用 ID; - 将
main分支推送作为部署触发器。
需要留意的是示例中的 supabase functions deploy --project-ref $PROJECT_ID 并未指定函数名——从 Supabase CLI v1.62.0 起支持单条命令部署项目内全部函数,这正是本工作流能一次发布所有函数的原因。
从客户端调用函数
部署完成后,即可通过官方客户端库发起调用。仓库内测试客户端使用的是 supabase-js 的 invoke 方法(参见 app/src/App.js 中的 supabase.functions.invoke(supaFunction, { body })),调用时 supabase-js 会自动附加当前登录用户的 Access Token,因此 verify_jwt = true 的函数开箱即可鉴权通过:
const { data, error } = await supabase.functions.invoke('browser-with-cors', {
body: { name: 'world' },
})
supabase-dart 等其他语言客户端也提供了对应的 invoke 能力,更多客户端生态可在 supabase-community 组织下关注更新。至此,从本地联调到云端部署再到客户端调用的完整闭环已经打通:本地用 supabase functions serve 迭代,线上用 supabase functions deploy 发布,浏览器测试客户端 app 同时服务于本地与云端两种场景,而 config.toml 与 GitHub Actions 则分别保证了函数级配置的一致性和发布的自动化。
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