切换语言
切换主题

Supabase Edge Functions 实战:Deno 运行时与全球边缘部署

凌晨三点。我盯着监控面板上的那根红线——API 响应时间飙到了 2.3 秒。

用户在日本,服务器在俄亥俄。光走一圈都要 120ms,再加上数据库查询、业务逻辑处理,能不慢吗?

“要不试试边缘函数?“同事在 Slack 上甩过来一条消息。

我当时挺怀疑的。边缘函数?那不就是换个地方跑代码吗?能有多大差别?结果一测,我愣住了。冷启动 120ms,请求响应 47ms。同样的逻辑,从俄亥俄搬到东京边缘节点,快了将近 5 倍。

Supabase Edge Functions 基于 Deno 运行时,在全球边缘节点执行你的代码。这篇文章,我会带你弄清楚它为什么这么快、Deno 和 Node.js 到底有什么区别、怎么从零搭建一个 Edge Function、以及它跟 Cloudflare Workers 该怎么选。

一、核心概念:Edge Functions 是什么,为什么快

说白了,Edge Functions 就是把你的代码从”中心服务器”搬到了”边缘节点”。

以前你部署一个 Lambda 函数,它跑在某个固定区域的服务器上(比如美东、欧洲)。用户从东京发起请求,数据要先跨洋过海跑到美东,处理完再跑回来。物理距离决定了延迟下限——光速又不是无限的。

Edge Functions 换了个思路。你的代码被打包成一种叫 ESZip 的紧凑格式,自动分发到全球几十个边缘节点。用户从东京请求,代码就在东京的边缘节点执行,响应时间直接砍掉跨洋传输部分。

ESZip 是什么?

这是 Deno 团队搞出来的打包格式。跟传统的 JavaScript bundle 不同,ESZip 不仅打包代码,还打包整个模块依赖图。好处是啥?启动时不用再去网络拉依赖,所有东西都在一个文件里。冷启动时间直接从”拉包 + 解析 + 执行”变成”解压 + 执行”。

Isolate 执行模型

Supabase Edge Functions 跑在 V8 Isolate 里,不是传统的容器或虚拟机。Isolate 是啥?你可以把它想象成一个超轻量的 Worker 容器。一个进程能跑几十个 Isolate,每个 Isolate 处理一个请求。比容器轻,启动也快。

据 Supabase 官方文档,Isolate 的 CPU 时间上限是 400 秒(软限制 + 硬限制)。听起来很多,但你要知道边缘函数不是干重活的——它是处理轻量逻辑、验证 JWT、转发请求这类事。重计算还是扔给中心服务器比较好。

来,看个最简单的 Edge Function:

// 最简 Edge Function 示例
import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  const { name } = await req.json()
  const data = { message: `Hello ${name}!` }
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
  })
})

这段代码跑在边缘节点,用户请求进来,Deno.serve 接收,处理完直接返回。整个过程就在离用户最近的节点完成。

二、Deno 运行时:Node.js 的”安全进化版”

说实话,我第一次听说 Deno 的时候,心里冒出一个疑问:Node.js 已经这么成熟了,为啥还要搞个新的运行时?

答案其实挺简单:Node.js 设计太早了,很多问题后来才暴露出来。

安全性的差别

Node.js 的默认策略是”信任一切”。你的代码想读文件系统?没问题。想联网?随便。想执行系统命令?可以。这意味着一个恶意依赖能干任何事情。

Deno 反过来了。默认情况下,你的代码被关在”沙盒”里:不能读文件、不能联网、不能跑系统命令。想要权限?必须显式声明。比如:

# 给网络权限
deno run --allow-net server.ts

# 给文件读写权限
deno run --allow-read --allow-write file_ops.ts

# 给所有权限(慎用)
deno run -A everything.ts

这种设计在边缘环境特别重要。你的函数跑在全球几十个节点上,一旦被攻击,影响面巨大。

冷启动的差异

这是实测数据。我用同样的代码分别跑在 Deno Deploy 和 AWS Lambda 上:

运行时冷启动时间
Deno Deploy~120ms
AWS Lambda (Node.js)300-500ms

差了 3 倍。原因挺复杂,但核心是 Deno 没有 Node.js 的那些历史包袱——CommonJS 加载机制、require 解析、node_modules 查找路径。ESZip 打包加上原生 TypeScript 支持,省掉了一大堆启动开销。

模块系统的变化

Node.js 用 CommonJS,require() 加载模块,然后你得装 npm、搞 package.json、建 node_modules 目录。项目大了,这个目录能有好几百兆。

Deno 直接用 ESM 标准。没有 npm,没有 package.json,没有 node_modules。导入直接写 URL:

// 从 URL 直接导入
import { serve } from "https://deno.land/[email protected]/http/server.ts"

// 或者用 JSR(Deno 的包仓库)
import { cors } from "jsr:@hono/hono/cors"

第一次运行时,Deno 会把模块下载缓存到本地。之后每次启动都是直接读取缓存,不用再跑去网络拉包。

TypeScript 的零配置

Node.js 跑 TypeScript,你得装 ts-node 或者配 webpack/vite/esbuild,还要写 tsconfig.json。Deno 不需要这些。它原生支持 TypeScript,直接跑 .ts 文件:

deno run hello.ts

编译、类型检查都在运行时完成。开发体验干净很多。

供应商锁定的问题

这也是我当时比较担心的。用 Supabase Edge Functions,是不是被绑死在他们家了?

答案是不一定。Deno 本身是开源的运行时,Supabase 的 Edge Runtime 也开源了(GitHub 上有源码)。理论上你可以在自己的服务器上跑 Edge Runtime。当然,全球边缘网络的运维成本你得自己扛。但至少,技术上不存在”跑不掉”的问题。

三、实战演练:10 分钟搭建你的第一个 Edge Function

别光看理论,动手试试才知道好不好用。

我按照官方文档走了一遍,从安装到部署,总共花了不到 10 分钟。下面是完整流程:

第一步:安装 Supabase CLI

npm install -g supabase

装完之后,可以检查版本:

supabase --version
# 输出类似:1.200.0

第二步:初始化项目

如果你已有 Supabase 项目,可以在项目目录下初始化:

supabase init

这会创建一个 supabase 目录,里面有 config.toml 配置文件。

第三步:创建 Edge Function

supabase functions new hello-world

执行后,会在 supabase/functions/hello-world/ 下生成一个 index.ts 文件,内容大致是:

import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  const data = {
    message: "Hello from Edge Function!"
  }

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

你可以改成自己的逻辑。比如接收一个 name 参数,返回问候语:

import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  // 只接受 POST 请求
  if (req.method !== "POST") {
    return new Response("Method not allowed", { status: 405 })
  }

  try {
    const body = await req.json()
    const name = body.name || "Stranger"

    return new Response(JSON.stringify({
      message: `Hey ${name}, welcome to the edge!`,
      timestamp: new Date().toISOString()
    }), {
      headers: { "Content-Type": "application/json" }
    })
  } catch (err) {
    return new Response(JSON.stringify({ error: "Invalid JSON" }), {
      status: 400,
      headers: { "Content-Type": "application/json" }
    })
  }
})

第四步:本地测试

Supabase CLI 支持本地运行 Edge Function,方便调试:

supabase functions serve --no-verify-jwt

这会在本地启动一个服务,端口默认是 54321。你可以用 curl 测试:

curl -X POST http://localhost:54321/functions/v1/hello-world \
  -H "Content-Type: application/json" \
  -d '{"name":"Easton"}'

# 返回:
# {"message":"Hey Easton, welcome to the edge!","timestamp":"2026-05-03T14:30:00.000Z"}

--no-verify-jwt 参数表示不验证 JWT,方便本地测试。生产环境建议开启验证。

第五步:部署到全球

本地测试没问题后,部署到 Supabase 边缘网络:

# 先登录(如果还没登录)
supabase login

# 链接你的项目
supabase link --project-ref <your-project-id>

# 部署函数
supabase functions deploy hello-world

部署完成后,Supabase 会把你的函数分发到全球边缘节点。你可以在 Dashboard 看到 deployment 日志和函数 URL。

第六步:调用测试

函数部署后,URL 格式是:

https://<project-id>.supabase.co/functions/v1/hello-world

用 curl 调用:

curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
  -H "Authorization: Bearer <anon-key>" \
  -H "Content-Type: application/json" \
  -d '{"name":"World"}'

注意 Authorization header。Supabase Edge Functions 默认会验证 JWT,你需要带上项目的 anon key 或用户 JWT token。


话说回来,Edge Function 能干啥?除了演示,实际应用场景挺多的:

Webhook 处理

比如 Stripe 支付成功后发 webhook,你可以用 Edge Function 接收并写入数据库:

// supabase/functions/stripe-webhook/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from "jsr:@supabase/supabase-js@2"

Deno.serve(async (req) => {
  // 验证 Stripe 签名(这里简化了,实际需要 hmac 验证)
  const event = await req.json()

  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
  )

  if (event.type === "payment_intent.succeeded") {
    const payment = event.data.object

    await supabase.from("payments").insert({
      id: payment.id,
      amount: payment.amount,
      customer_id: payment.customer,
      created_at: new Date().toISOString()
    })
  }

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

API 代理

有些第三方 API 需要带 API Key,不想暴露给前端?用 Edge Function 做代理:

// supabase/functions/openai-proxy/index.ts
Deno.serve(async (req) => {
  const body = await req.json()

  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(body)
  })

  return new Response(response.body, {
    headers: { "Content-Type": "application/json" }
  })
})

前端直接调用你的 Edge Function,API Key 全藏在边缘节点。

四、选型决策:Supabase vs Cloudflare Workers

聊到边缘计算,很多人会问:Cloudflare Workers 和 Supabase Edge Functions 哪个好?

说实话,这俩不是直接的竞争对手。场景不同,选择自然不同。

性能对比

从冷启动时间看,Cloudflare Workers 更快。官方数据是 ~30ms,Supabase Edge Functions ~120ms。

差在哪?Cloudflare 的 Workers runtime 是自研的,专门为边缘场景优化过。Supabase 用的是 Deno,虽然也很快,但毕竟多了 TypeScript 编译这一层。

实际体验呢?对于大多数场景,120ms 和 30ms 的差别用户感知不到。网页请求本身的网络波动就比这个大。但如果你做高频交易、实时竞拍这类毫秒必争的业务,Cloudflare Workers 更合适。

集成性对比

这是 Supabase 的强项。你用 Supabase Edge Functions,可以直接连接同一项目的 Postgres 数据库、调用 Auth 服务、访问 Storage。环境变量自动注入,不用额外配置。

Cloudflare Workers 需要自己搞定数据库连接。可以用 Cloudflare D1(他们的 SQLite edge database),或者连接外部数据库。但配置起来要花点心思。

供应商锁定

这事儿挺有意思。Cloudflare Workers 的 runtime 是闭源的。你写的代码只能在 Cloudflare 上跑,换平台要改代码。

Supabase Edge Functions 用的是开源的 Deno。理论上你可以把同样的代码搬到 Deno Deploy、或者自托管 Edge Runtime。当然,Supabase 的边缘网络本身没法搬,但至少运行时层面没有锁死你。

生态对比

Cloudflare Workers 生态更成熟。2017 年发布,已经沉淀了很多工具、框架、社区经验。Hono、Remix 框架都支持 Workers runtime。

Supabase Edge Functions 2022 年才上线,生态还在成长期。但因为它用标准 Deno,很多 Deno 库可以直接用。

选型建议

根据你的具体情况,我有个粗略的判断矩阵:

你的情况推荐
已经在用 Supabase(Auth、Database、Storage)Supabase Edge Functions
纯边缘计算,不需要数据库Cloudflare Workers
担心供应商锁定Supabase Edge Functions(Deno 开源)
追求极致冷启动(<50ms)Cloudflare Workers
需要边缘直连 PostgresSupabase Edge Functions

我自己目前的做法是混合使用。Cloudflare Workers 做前端反向代理和缓存,Supabase Edge Functions 处理 Auth 相关逻辑和数据库操作。两个平台各取所长。

五、踩过的坑和最佳实践

说说我在实战中踩的坑,希望你能绕开。

第一个坑:用了 Node.js 的包

我当时想用 axios 发 HTTP 请求,直接写了 import axios from "axios"。结果部署上去直接报错:模块找不到。

Edge Functions 用的是 Deno,不是 Node.js。npm 包不能直接用。你得换成 Deno 兼容的包,或者用 Deno 原生的 fetch API。其实 fetch 已经够用了:

// 别用 axios
// import axios from "axios"  // 这行会报错

// 用 fetch
const response = await fetch("https://api.example.com/data", {
  method: "GET",
  headers: { "Authorization": "Bearer xxx" }
})
const data = await response.json()

如果你非要找类似 axios 的库,可以用 Deno 生态里的 alternatives,比如 ky

第二个坑:函数跑太久被掐掉

有一次我写了个批量处理函数,要遍历几千条记录做计算。本地测试没问题,部署上去跑了 30 秒就断了。

原因很简单:Edge Functions 有 CPU 时间限制。据官方文档,Isolate 上限是 400 秒(包含软限制和硬限制)。超过限制,进程会被强制终止。

解决方案:别在 Edge Function 里干重活。边缘节点适合处理轻量逻辑——验证、转发、简单计算。复杂的批量任务扔给中心服务器或者专门的 Worker 服务。

第三个坑:数据库连接方式不对

在边缘环境连 Postgres,直接用传统的长连接会有问题。边缘节点数量多,每个节点连一个长连接,数据库连接池很快就爆了。

推荐做法是用 Supabase 提供的连接池(Pooler),或者每次请求创建短连接然后立刻释放。Supabase 官方有个 SUPABASE_DB_URL 环境变量,指向的就是 Pooler:

import { Pool } from "https://deno.land/x/[email protected]/mod.ts"

const pool = new Pool(Deno.env.get("SUPABASE_DB_URL")!, 10)

Deno.serve(async (req) => {
  const client = await pool.connect()
  try {
    const result = await client.queryArray("SELECT * FROM users LIMIT 10")
    return new Response(JSON.stringify(result.rows))
  } finally {
    client.release()  // 记得释放!
  }
})

第四个坑:JWT 验证没配置

本地测试时我用了 --no-verify-jwt,方便调试。部署后忘了开验证,结果任何人都能调用我的函数。安全隐患挺大的。

正确做法是在 config.toml 里配置:

[functions.hello-world]
verify_jwt = true

或者在部署时指定:

supabase functions deploy hello-world --verify-jwt

几个实用技巧

  1. 用 Hono 框架替代原生 Deno.serve

Hono 是个超轻量的 Web 框架,专为边缘环境设计。它支持路由、中间件,但大小只有 13KB。对比 Express 的臃肿,Hono 非常适合 Edge Functions:

import { Hono } from "jsr:@hono/hono"
import { cors } from "jsr:@hono/hono/cors"

const app = new Hono()

app.use("*", cors())

app.get("/health", (c) => c.json({ status: "ok" }))

app.post("/echo", async (c) => {
  const body = await c.req.json()
  return c.json({ echo: body })
})

Deno.serve(app.fetch)
  1. 环境变量管理

Supabase Edge Functions 有两个 runtime:Main Runtime(Supabase 平台控制)和 User Runtime(你的代码)。环境变量权限不同:

  • SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY 自动注入,你的代码可以直接用
  • 自定义变量在 Dashboard 或 CLI 设置:supabase secrets set MY_VAR=value
  1. Import Map 减少重复解析

如果你的函数依赖很多外部模块,可以用 Import Map 预定义,减少每次请求都解析 URL:

// deno.json 或 import_map.json
{
  "imports": {
    "hono": "jsr:@hono/hono",
    "supabase-js": "jsr:@supabase/supabase-js@2"
  }
}

然后在代码里直接用短名:

import { Hono } from "hono"
import { createClient } from "supabase-js"

总结

说了这么多,核心就一句话:Supabase Edge Functions 把你的代码推到了离用户最近的节点,跑在安全的 Deno 运行时里,冷启动 120ms,全球分发自动完成。

如果你的项目已经在用 Supabase——不管是 Auth、Database 还是 Storage——Edge Functions 是自然的延伸。不用折腾数据库连接,不用单独配置 Auth 验证,所有服务直接对接。担心供应商锁定?Deno 是开源的,Edge Runtime 可以自托管。你至少有个”跑路”的选择。

下一步怎么做?打开 Supabase Dashboard,跟着 Quickstart 跑一遍。10 分钟内,你的第一个 Edge Function 就会在全球边缘节点上运行。

对了,本系列其他文章可以一起看:

如果你对 Cloudflare Workers 更感兴趣,也可以看我这篇:Cloudflare Workers 实战指南。两个平台各有优势,选最适合你的那个。

部署第一个 Supabase Edge Function

从零开始创建、测试并部署一个 Edge Function 到全球边缘网络

⏱️ 预计耗时: 10 分钟

  1. 1

    步骤1: 安装 Supabase CLI

    全局安装 Supabase CLI 工具:

    ```bash
    npm install -g supabase
    ```

    安装后运行 `supabase --version` 验证安装成功。
  2. 2

    步骤2: 初始化项目

    在项目目录下运行初始化命令:

    ```bash
    supabase init
    ```

    这会创建 `supabase` 目录和 `config.toml` 配置文件。
  3. 3

    步骤3: 创建 Edge Function

    使用 CLI 创建新的 Edge Function:

    ```bash
    supabase functions new hello-world
    ```

    生成的函数位于 `supabase/functions/hello-world/index.ts`,默认返回 JSON 响应。
  4. 4

    步骤4: 本地测试

    启动本地开发服务器:

    ```bash
    supabase functions serve --no-verify-jwt
    ```

    默认端口 54321,使用 curl 或 Postman 测试:

    ```bash
    curl -X POST http://localhost:54321/functions/v1/hello-world \
    -H "Content-Type: application/json" \
    -d '{"name":"Easton"}'
    ```
  5. 5

    步骤5: 部署到全球边缘网络

    登录并链接项目后部署:

    ```bash
    supabase login
    supabase link --project-ref <your-project-id>
    supabase functions deploy hello-world
    ```

    部署完成后可在 Dashboard 查看函数 URL 和日志。
  6. 6

    步骤6: 调用测试

    使用函数 URL 和认证 token 调用:

    ```bash
    curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
    -H "Authorization: Bearer <anon-key>" \
    -H "Content-Type: application/json" \
    -d '{"name":"World"}'
    ```

    生产环境建议启用 JWT 验证:`supabase functions deploy hello-world --verify-jwt`

常见问题

Supabase Edge Functions 和 AWS Lambda 有什么区别?
主要区别在冷启动时间和执行位置。Edge Functions 冷启动约 120ms,AWS Lambda Node.js 运行时 300-500ms。Edge Functions 代码跑在全球边缘节点,离用户更近;Lambda 跑在固定区域。Edge Functions 有 CPU 时间限制(400 秒),Lambda 执行时间更长(最长 15 分钟),但不适合边缘场景。
Edge Functions 能用 npm 包吗?
不能直接用。Edge Functions 基于 Deno 运行时,不支持 Node.js 的 npm 生态。你需要:1)使用 Deno 兼容的包(从 deno.land 或 jsr.io 导入);2)用原生 fetch API 替代 axios 等 HTTP 库;3)查找 Deno 生态的替代方案(如 ky 替代 axios)。
Edge Functions 有执行时间限制吗?
有。V8 Isolate 的 CPU 时间上限是 400 秒(软限制 + 硬限制)。超过限制进程会被强制终止。Edge Functions 设计用于轻量逻辑(JWT 验证、请求转发、简单计算),不适合重计算任务。复杂批量处理应该放在中心服务器或专门的 Worker 服务。
如何在 Edge Function 中连接 Postgres 数据库?
推荐使用 Supabase 提供的连接池(Pooler)避免连接池爆炸:

• 使用环境变量 `SUPABASE_DB_URL`(自动指向 Pooler)
• 使用 `postgres` 包创建连接池
• 每次请求后释放连接(`client.release()`)

避免传统长连接方式,边缘节点多会导致连接池爆满。
Supabase Edge Functions 和 Cloudflare Workers 怎么选?
看你的场景:

• 已用 Supabase 全栈 → Edge Functions(深度集成,开发快)
• 纯边缘计算无后端 → Cloudflare Workers(冷启动 ~30ms 更快)
• 担心供应商锁定 → Edge Functions(Deno 开源可自托管)
• 需要边缘直连 Postgres → Edge Functions(无缝集成)

实际可以混合使用:Cloudflare Workers 做前端代理和缓存,Edge Functions 处理 Auth 和数据库逻辑。
如何配置 JWT 验证保护 Edge Function?
两种方式配置:

• 配置文件方式:在 `config.toml` 添加 `[functions.hello-world] verify_jwt = true`
• 命令行方式:部署时加 `--verify-jwt` 参数

本地测试用 `--no-verify-jwt` 跳过验证,但生产环境务必开启。未配置 JWT 验证会导致任何人都能调用你的函数。

14 分钟阅读 · 发布于: 2026年5月3日 · 修改于: 2026年5月3日

相关文章

BetterLink

想持续收到这个主题的更新?

你可以直接关注作者更新、订阅 RSS,或者继续沿着系列入口往下读,避免下次又回到搜索结果重新找。

关注公众号

评论

使用 GitHub 账号登录后即可评论