摘要
本文档旨在为使用 Supabase Edge Functions 的开发者提供一份全面的开发指南。Edge Functions 是一种在离用户更近的边缘服务器上运行的 TypeScript 函数,具有低延迟和高响应速度的优势。它们基于现代的 Deno 运行时环境,这与传统的 Node.js 后端开发在环境、安全性和依赖管理上有着显著的区别。本文档将首先介绍 Deno 的核心概念,然后深入探讨编写 Edge Functions 时的关键注意事项、最佳实践和工作流程。
Deno - Edge Functions 的核心运行环境
要精通 Supabase Edge Functions,首先必须理解其基础------Deno。Deno 是由 Node.js 创造者 Ryan Dahl 开发的一个现代、安全的 JavaScript 和 TypeScript 运行时,旨在修复 Node.js 的一些历史设计缺陷。
1.1 Deno 是什么?
- 一个运行时:和 Node.js 一样,它让 JavaScript/TypeScript 代码可以在服务器端运行。
- Node.js 的继任者:它被设计为 Node.js 的一个更现代、更安全的替代品。
1.2 Deno 与 Node.js 的核心区别
特性 (Feature) | Deno | Node.js |
---|---|---|
安全性 | 默认沙箱,需通过命令行标志显式授权文件、网络等权限 | 默认拥有与启动进程相同的权限 |
TypeScript 支持 | 原生支持 ,可直接运行 .ts 文件,无需任何配置 |
需要手动安装编译器 (tsc ) 和配置 |
模块管理 | URL 导入 (ESM) ,无 node_modules 文件夹 |
使用 npm /yarn ,package.json ,依赖存储在 node_modules |
内置工具 | 非常全面 (代码格式化、检查、测试、打包等) | 依赖庞大且分散的第三方工具生态 |
核心 API | 拥抱 Web 标准 (如 fetch , Request , Response , URL ) |
拥有大量自有 API (如 require , fs , http 模块) |
生态系统 | 正在快速成长,但相对较小 | 极其庞大和成熟 |
理解这些区别至关重要,因为它们直接影响了您编写、管理和部署 Edge Functions 的方式。
编写 Supabase Edge Functions 的关键注意事项
基于 Deno 的特性,开发 Edge Functions 时需要遵循一套新的规则和最佳实践。
2.1 核心概念与运行环境
-
没有
npm
,拥抱 URL 导入 您的所有依赖都必须通过 URL 引入。这是 Deno 的核心机制。TypeScript// 正确方式: 直接从 URL 导入 import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
-
使用
import_map.json
管理依赖 为了代码整洁和版本管理,最佳实践是使用supabase/import_map.json
文件来定义依赖别名。JSON// supabase/import_map.json { "imports": { "supabase-js": "https://esm.sh/@supabase/supabase-js@2", "stripe": "https://esm.sh/stripe@11.1.0?target=deno" } }
在代码中即可像传统方式一样导入:
TypeScriptimport { createClient } from "supabase-js";
-
原生 TypeScript 与 Web API 直接编写
.ts
文件,无需编译步骤。函数内可以直接使用fetch
,Request
,Response
等浏览器标准 API,开发体验与编写 Service Worker 类似。
2.2 安全性 (Security)
安全性是 Edge Functions 的生命线。
-
用户身份验证与 RLS 客户端调用函数时,其 JWT 会通过
Authorization
请求头传递。您必须在函数中解析此 Token,并用它来初始化 Supabase 客户端,这样才能确保数据库的行级安全策略(RLS)对该用户的操作生效。TypeScriptconst authHeader = req.headers.get('Authorization')!; // 以用户的身份创建客户端,所有操作将遵循该用户的 RLS const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '', { global: { headers: { Authorization: authHeader } } } ); // 此查询将受到 RLS 的限制 const { data, error } = await supabaseClient.from('profiles').select('*');
-
谨慎使用服务角色密钥 (
service_role
key) 此密钥可以绕过所有 RLS 策略 ,拥有数据库的完全权限。绝对禁止将其硬编码在代码中。-
正确做法:通过 Supabase Secrets 进行管理。
Bash# 本地设置 supabase secrets set MY_SERVICE_KEY=your_secret_key # 生产环境在项目面板中设置
-
在代码中通过环境变量安全访问:
TypeScript// 创建一个拥有管理员权限的客户端 const supabaseAdmin = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '' // 安全获取 );
-
-
处理 CORS (跨域资源共享) 如果函数需要被浏览器前端调用,必须正确处理 CORS。这通常包括响应
OPTIONS
预检请求和在主请求的响应中添加Access-Control-Allow-Origin
等头部信息。
2.3 性能与限制
- 无状态 (Stateless) :每次函数调用都在一个全新的、隔离的环境中执行。两次调用之间不共享内存或本地状态。持久化数据需依赖 Supabase 数据库或其他外部服务。
- 冷启动 (Cold Starts) :函数长时间未被调用后,首次调用会有轻微的启动延迟。保持函数代码和依赖轻量化是关键。
- 执行时间限制:函数有最大执行时间(通常为 60 秒),不适合长时间运行的批处理任务。
- 资源限制:存在内存使用上限和部署包大小限制。
- 无文件系统访问:无法像在传统服务器上那样读写本地文件系统。
2.4 开发与部署工作流
Supabase CLI 是您的核心开发工具。
-
启动本地环境:
Bashsupabase start
这会在本地运行一个完整的 Supabase 服务栈。
-
本地开发与测试:
Bashsupabase functions serve <function_name>
此命令会启动一个本地服务器来运行您的函数,并支持热重载,极大提升开发效率。
-
部署到生产环境:
Bashsupabase functions deploy <function_name>
-
日志与调试:
- 本地 : 日志 (
console.log
) 会直接输出到运行serve
命令的终端。 - 生产: 在 Supabase 项目的 Dashboard 中查看函数的调用日志。
- 本地 : 日志 (