首页
/ Supabase Edge Functions + Resend 实战:用 send-email-resend 在边缘发送事务邮件

Supabase Edge Functions + Resend 实战:用 send-email-resend 在边缘发送事务邮件

2026-09-06 18:26:09作者:温玫谨Lighthearted

导读

本文以仓库 examples/edge-functions 中的 send-email-resend 示例 为核心,讲解如何基于 Supabase Edge Functions(Deno 运行时)调用 Resend 邮件 API 发送事务邮件:从准备 API Key 与域名校验、配置本地 .env,到用 Supabase CLI 本地起服务、无 JWT 联调,最后部署到云端。读完你将掌握一个可运行的最小发信函数,并理解其 fetch 处理器的关键实现、config.tomlverify_jwt 的取舍逻辑,以及同一目录下向 SMTP、React Email 模板方案演进的方向。

示例定位:仓库中与发信相关的三个示例

在阅读具体实现前,先明确 send-email-resend 在仓库中的位置。整个示例集合位于 examples/edge-functions/supabase/functions,发信相关共有三个互相补充的目录:

  • send-email-resend:本文主角,最简实现,直接以原生 fetch 调用 https://api.resend.com/emails,用于理解 Resend 发送 API 的最小协议;
  • send-email-smtp:基于 nodemailer 走通用 SMTP 协议,适合有自建 SMTP/其他邮件服务商的场景;
  • auth-hook-react-email-resend:引入 Resend 官方 SDK(resend@^6)与 @react-email/render,配合 Supabase Auth 的 Send Email Hook,用 React 模板渲染 HTML 邮件。

本文聚焦第一个示例,但会在最后一节横向对比三者,方便你按需取舍。

前置条件

根据 send-email-resend/README.md 的 Prerequisites,运行该示例前需要完成三件事:

  1. 在 Resend 后台创建 API Key,用于后续的身份认证;
  2. 在 Resend 后台完成域名验证(Domain verification),保证发送身份可信(示例代码默认走 @resend.dev 测试域名,实际生产务必换成已验证域名);
  3. 准备本地 .env 文件并写入 RESEND_API_KEY

仓库在 examples/edge-functions/supabase/.env.local.example 中为整个示例集合提供了统一的本地环境变量模板,其中与本文相关的配置段为:

# send-email-resend
RESEND_API_KEY=

创建本地环境文件的方式,与仓库根 README 推荐的一致:

cp .env.example .env

若按整个 edge-functions 仓库的开发流程走,则是:

cp ./supabase/.env.local.example ./supabase/.env.local

注意 .env 属于敏感文件,应加入 .gitignore,避免将密钥提交进仓库。除 RESEND_API_KEY 外,同一个模板文件里还列出了 SMTP_*OPENAI_API_KEYSTRIPE_API_KEY 等其他示例共用的密钥,你只需关心与当前函数相关的条目。

源码拆解:一个 25 行的最小发信函数

整个函数只有一份实现文件 send-email-resend/index.ts,完整代码如下:

import { withSupabase } from 'npm:@supabase/server@^1'

const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')

// Accepts a signed-in user's JWT or a secret key, so deploy with verify_jwt = false.
export default {
  fetch: withSupabase({ auth: ['user', 'secret'] }, async (req, ctx) => {
    const res = await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${RESEND_API_KEY}`,
      },
      body: JSON.stringify({
        from: 'onboarding@resend.dev',
        to: 'delivered@resend.dev',
        subject: 'hello world',
        html: '<strong>it works!</strong>',
      }),
    })

    const data = await res.json()

    return Response.json(data)
  }),
}

逐行解读其中值得借鉴的关键点:

  • 入口方式:Supabase Edge Functions 在 Deno 上运行,导出的是带 fetch 方法的默认对象。withSupabase 来自 npm:@supabase/server@^1(本目录 README 之外仓库其他函数也大量使用),是 Supabase 提供的一个高层封装,用于把传入请求与 Supabase 上下文(auth、database client 等)绑定。这里通过 auth: ['user', 'secret'] 显式声明:该函数同时接受已登录用户的 JWTService Role 这类 secret key
  • 密钥读取Deno.env.get('RESEND_API_KEY') 在模块加载时读取环境变量。如果本地漏配 .env.local 或云端漏配 Secret,该值会是 undefined,发出的请求将因缺少 Bearer 凭据被 Resend 拒绝——排查发信失败时,这是最先要检查的点。
  • 对接 Resend HTTP API:使用标准 fetchPOST https://api.resend.com/emails 发送 JSON,鉴权方式为请求头 Authorization: Bearer <RESEND_API_KEY>。请求体中 from/to 两个字段指向 Resend 官方测试地址(onboarding@resend.devdelivered@resend.dev),html 字段承载邮件正文。
  • 返回响应res.json() 读取 Resend 返回的载荷(成功时含邮件 id,失败时含错误信息),再经 Response.json(data) 以 JSON 形式回给调用方,便于本地 curl 直接核对结果。

send-email-smtp/index.ts 对比可明显看出两种集成路线的差异:SMTP 版通过 nodemailer.createTransport(...) 建立传输通道后用 transport.sendMail(...) 发送;而 Resend 版不依赖任何 SDK,一个 fetch 即完成投递,体积最小、依赖最少。

本地运行与联调

README 的 Instructions 给出了两条本地命令,前提是已安装最新版 Supabase CLI

supabase start
supabase functions serve --no-verify-jwt --env-file ./supabase/.env.local

各命令作用如下:

  • supabase start:在本机(借助 Docker)拉起整套本地 Supabase 栈。本仓库对应的 config.toml 中配置了本地 API 端口 54321、数据库端口 54322 等,启动后可提供本地函数网关。
  • supabase functions serve --no-verify-jwt --env-file ./supabase/.env.local:启动本地函数开发服务器,--env-file 指向我们上一步填好 RESEND_API_KEY.env.local--no-verify-jwt 表示跳过 JWT 校验,方便在浏览器或 curl 中直接调用调试。

服务起来后,直接在浏览器或 curl 访问 README 给出的本地端点即可触发发信:

GET http://localhost:54321/functions/v1/send-email-resend

在示例集合根 README 中可以看到,仓库推荐的本地开发流程与此完全一致:先 supabase start,再复制 .env.local 并填入对应密钥,然后 supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt,最后用函数示例内的 CURL 命令或 Supabase 客户端库的 invoke 方法发起测试请求。这说明 send-email-resend 完全可以复用整个仓库的标准本地链路。

部署到 Supabase 云端

本地验证通过后,按 README 执行部署命令:

supabase functions deploy resend --no-verify-jwt

需要指出两处与函数实际命名的细节:

  1. 函数源码位于目录 functions/send-email-resend,而 README 的部署命令写作 deploy resend。以目录名 send-email-resend 部署(supabase functions deploy send-email-resend)与该函数实际的本地 URL 路径 /functions/v1/send-email-resend 保持一致,是更稳妥的做法;若使用 --no-verify-jwt 部署,建议显式声明该函数不校验 JWT,避免与前端场景错配。
  2. 云端密钥需要在部署前设置。仓库根 README 给出的标准做法是:
supabase secrets set --env-file ./supabase/.env.local

它会将 .env.local 中的全部变量(含 RESEND_API_KEY)同步为线上 Secret;也可用 supabase secrets list 验证是否写入成功。生产环境建议单独维护一个不含本地杂项变量的 .env 文件用于云端。

部署配置佐证:config.toml 中的 verify_jwt

为什么 README 反复强调 --no-verify-jwt?查看本仓库的 config.toml 可以找到一致性的配置证据。该文件为每个示例函数都声明了独立配置段,其中:

[functions.send-email-resend]
verify_jwt = false

即该函数在 Supabase Functions 平台上的默认 JWT 校验开关被显式关闭。这与源码注释 // Accepts a signed-in user's JWT or a secret key, so deploy with verify_jwt = false. 互为印证:withSupabase({ auth: ['user', 'secret'] }, ...) 已经自行完成了对「用户 JWT 或 secret key」的鉴权,若网关层再强制校验 JWT,会导致携带 Secret 的服务器端调用被拦在门外,因此必须 verify_jwt = false。同理,需要登录态才允许调用的 send-email-smtpconfig.toml 中对应段则是 verify_jwt = true,两种配置形成鲜明对照,可作为你设计自己函数鉴权策略时的参考模板。

从测试域名走向生产:调用方鉴权与模板化进阶

作为演示,示例默认使用 Resend 的测试地址 onboarding@resend.dev / delivered@resend.dev。要真正对外发信,需满足两个前提:域名已通过 Resend 验证,且发件人替换为验证域名下的真实地址。

若你需要更完整的生产级发信链路,仓库内现成的同类示例可以直接迁移:

  • 鉴权模式对齐:想让该函数接受「任意已登录用户」调用,可参考 send-email-smtpwithSupabase 的鉴权收敛为 { auth: 'user' }(对应 verify_jwt = true),并通过 Authorization: Bearer <USER_ACCESS_TOKEN> 头调用。
  • HTML 邮件模板:参考 auth-hook-react-email-resend/index.ts,它使用 npm:resend@^6new Resend(Deno.env.get('RESEND_API_KEY'))resend.emails.send({ from, to, subject, html }),并用 @react-email/render + react@^19MagicLinkEmailSignUpEmail 这类 React 组件渲染成 HTML 正文;其部署同样配合 verify_jwt = false(由 Supabase Auth 以 Webhook 签名方式调用)。如果你需要在注册、登录等 Auth 事件时发送漂亮的自定义邮件,这是比手动拼 html 字符串更可维护的升级路线。

小结

通过 send-email-resend 示例,一条完整的「Supabase Edge Function + Resend 发信」链路已经跑通:Deno.env 读密钥 → withSupabase 包裹处理器 → fetch 调 Resend API → Response.json 回包;配合 .env.localsupabase functions servesupabase functions deployconfig.tomlverify_jwt 配置即可完成从本地到云端的部署。作为最薄的 Resend 集成样例,它天然适合作为你在 Supabase 平台上构建通知、验证码、事务邮件等能力的第一块积木。

登录后查看全文
热门项目推荐
相关项目推荐