Edge Functions 基于 /edge-functions 目录结构生成访问路由。您可在项目仓库 /edge-functions 目录下创建任意层级的子目录,参考下述示例。
...
edge-functions
├── index.js
├── hello-pages.js
├── helloworld.js
├── api
├── users
├── list.js
├── geo.js
├── [id].js
├── visit
├── index.js
├── [[default]].js
...
上述目录文件结构,经 EdgeOne Pages 平台构建后将生成以下路由。这些路由将 Pages URL 映射到 /edge-functions 文件,当客户端访问 URL 时将触发对应的文件代码被运行:
| 文件路径 | 路由 |
|---|---|
| /edge-functions/index.js | example.com/ |
| /edge-functions/hello-pages.js | example.com/hello-pages |
| /edge-functions/helloworld.js | example.com/helloworld |
| /edge-functions/api/users/list.js | example.com/api/users/list |
| /edge-functions/api/users/geo.js | example.com/api/users/geo |
| /edge-functions/api/users/[id].js | example.com/api/users/{id} |
| /edge-functions/api/visit/index.js | example.com/api/visit/ |
| /edge-functions/api/[[default]].js | example.com/api/{path} |
- 路由尾部斜杠 / 是可选。/hello-pages 和 /hello-pages/ 将被路由到 /edge-functions/hello-pages.js。
- 如果没有匹配到 Edge Functions 路由,客户端请求将被路由到 Pages 对应的静态资源。
- 路由大小写敏感,/helloworld 将被路由到 /edge-functions/helloworld.js,不能被路由到 /edge-functions/HelloWorld.js
Edge Functions 支持动态路由,上述示例中一级动态路径 /edge-functions/api/users/[id].js,多级动态路径 /edge-functions/api/[[default]].js。参考下述用法:
| 文件路径 | 路由 | 匹配 |
|---|---|---|
| /edge-functions/api/users/[id].js | example.com/api/users/1024 | 是 |
| /edge-functions/api/users/[id].js | example.com/api/users/vip/1024 | 否 |
| /edge-functions/api/users/[id].js | example.com/api/vip/1024 | 否 |
| /edge-functions/api/[[default]].js | example.com/api/1024 | 是 |
| /edge-functions/api/[[default]].js | example.com/api/books/list | 是 |
| /edge-functions/api/[[default]].js | example.com/v2/vip/1024 | 否 |
使用 Functions Handlers 可为 Pages 创建自定义请求处理程序,以及定义 RESTful API 实现全栈应用。支持下述的 Handlers 方法:
| Handlers 方法 | 描述 |
|---|---|
| onRequest(context: EventContext): Response Promise | 匹配 HTTP Methods (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS) |
| onRequestGet(context: EventContext): Response Promise | 匹配 HTTP Methods (GET) |
| onRequestPost(context: EventContext): Response Promise | 匹配 HTTP Methods (POST) |
context 是传递给 Function Handlers 方法的对象,包含下述属性:
params:动态路由 /edge-functions/api/users/[id].js 参数值
export function onRequestGet(context) {
return new Response(`User id is ${context.params.id}`);
}- env:Pages 环境变量
- waitUntil:(task: Promise): void; 用于通知边缘函数等待 Promise 完成,可延长事件处理的生命周期
request:客户端请求对象
Request 对象包含下述属性:
Request 代表 HTTP 请求对象,基于 Web APIs 标准 Request 进行设计。 边缘函数中,可通过两种方式获得 Request 对象:
- 使用 Request 构造函数创建一个 Request 对象,用于 Fetch API 的操作。
- 使用 FetchEvent 对象 event.request,获得当前请求的 Request 对象。
const request = new Request(input: string | Request, init?: RequestInit)| 参数名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input | string / Request | 是 | URL 字符串或 Request 对象。 |
| init | RequestInit | 否 | Request 对象初始化配置项。 |
初始化 Request 对象的属性值选项。
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| method | string | 否 | GET | 请求方法 (GET、POST 等)。 |
| headers | Headers | 否 | - | 请求头部信息。 |
| body | string | 否 | - | 请求体。 |
| redirect | string | 否 | follow | 重定向策略,支持 manual、error和 follow。 |
| maxFollow | number | 否 | 12 | 最大可重定向次数。 |
| version | string | 否 | HTTP/1.1 | HTTP 版本号。 |
| copyHeaders | boolean | 否 | - | 非 Web APIs 标准选项,表示是否拷贝传入的 Request 对象的 headers。 |
| eo | RequestInitEoProperties | 否 | - | 非 Web APIs 标准选项,用于控制边缘函数处理该请求的行为。 |
RequestInitEoProperties 是非 Web APIs 标准选项,用于控制边缘函数处理该请求的行为。
| 参数名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
| resolveOverride | string | 否 | 用于 fetch 请求下覆盖原有的域名解析, 支持指定域名或者 IP 地址。 IP 不允许带 scheme 以及端口号。 IPv6 无需使用方括号包裹。 |
| image | ImageProperties | 否 | 图片处理参数配置项。 |
body
// request.body
readonly body: ReadableStream;bodyUsed
// request.bodyUsed
readonly bodyUsed: boolean;headers
// request.headers
readonly headers: Headers;method
// request.method
readonly method: string;redirect
// request.redirect
readonly redirect: string;maxFollow
// request.maxFollow
readonly maxFollow: number;url
// request.url
readonly url: string;version
// request.version
readonly version: string;eo
// request.eo
readonly eo: IncomingRequestEoProperties;Node Functions 旨在简化您的后端开发流程,提供与前端项目无缝集成的 Node.js 运行时。它不仅支持处理传统的 HTTP 请求,还支持通过 WebSocket 协议进行实时双向通信。您可以在统一的项目中构建和部署动态 API 及复杂业务逻辑,平台将自动处理版本控制、构建与部署,并根据业务负载智能扩容,助您快速上线并持续交付。
- 丰富的 Node.js 生态:可以直接使用 npm 生态中的海量模块,轻松集成各类第三方库和工具,满足复杂业务需求。
- 全栈开发体验:无需再将前后端项目分离,可在同一个项目中完成开发和部署,大幅提升协作效率。
- 路由即服务:通过文件系统即可定义 API 路由,实现后端逻辑的快速开发与部署,将后端服务像前端页面一样便捷管理。
- 支持实时通信:内置对 WebSocket 协议的支持,可轻松构建实时聊天、数据推送等需要持久化连接的现代 Web 应用。
在项目的 ./node-functions/api 目录下新建 hello.js,使用以下示例代码创建您的第一个 Node Functions:
// 文件路径 ./node-functions/api/hello.js
// 访问路径 example.com/api/hello
export default function onRequest(context) {
return new Response('Hello from Node Functions!');
}警告 在 ./node-functions 目录下创建 index.js,访问根路径则会进入到该函数而非首页。 在 ./node-functions 目录下创建 [[id]].js,除根路径外其他所有路径都会进入到该函数,需在函数内处理静态资源的返回。
我们建议您通过子目录来管理函数文件,如下示例在 ./node-functions/api 下创建 nodeinfo.js,用于返回 node 相关信息:
// 文件路径 ./node-functions/api/nodeinfo.js
// 访问路径 example.com/api/nodeinfo
import * as os from 'node:os'
import { randomUUID, createHash } from 'node:crypto'
export const onRequestGet = async ({ request }) => {
const info = {
nodeVersion: process.version,
pid: process.pid,
platform: os.platform(),
url: request.url,
}
return new Response(JSON.stringify(info), {
status: 200,
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
})
}在 Node Functions 中使用 Express/Koa 开发时需注意,框架的路由只需要集中在一个函数文件里面处理,无需额外启动 HTTP Server 且需导出框架的实例。如下示例在 ./node-functions/express 下创建 [[default]].js,用于处理 express 的业务逻辑:
// 文件路径 ./node-functions/express/[[default]].js
import express from "express";
const app = express();
// 添加日志中间件
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// 添加根路由处理,访问路径 example.com/express/
app.get("/", (req, res) => {
res.json({ message: "Hello from Express on Node Functions!" });
});
// 访问路径 example.com/express/users/123
app.get("/users/:id", (req, res) => {
const { id } = req.params;
res.json({ userId: id, message: `Fetched user ${id}` });
});
// 导出 express 实例
export default app;Node Functions 基于 /node-functions 目录结构生成访问路由。您可在项目仓库 /node-functions 目录下创建任意层级的子目录,参考下述示例。
...
node-functions
├── index.js
├── hello-pages.js
├── helloworld.js
├── api
├── users
├── list.js
├── geo.js
├── [id].js
├── visit
├── index.js
├── [[default]].js
...
上述目录文件结构,经 EdgeOne Pages 平台构建后将生成以下路由。这些路由将 Pages URL 映射到 /node-functions 文件,当客户端访问 URL 时将触发对应的文件代码被运行:
| 文件路径 | 路由 |
|---|---|
| /node-functions/index.js | example.com/ |
| /node-functions/hello-pages.js | example.com/hello-pages |
| /node-functions/helloworld.js | example.com/helloworld |
| /node-functions/api/users/list.js | example.com/api/users/list |
| /node-functions/api/users/geo.js | example.com/api/users/geo |
| /node-functions/api/users/[id].js | example.com/api/users/1024 |
| /node-functions/api/visit/index.js | example.com/api/visit |
| /node-functions/api/[[default]].js | example.com/api/books/list |
| /node-functions/api/[[default]].js | example.com/api/books/1024 |
| /node-functions/api/[[default]].js | example.com/api/... |
说明 路由尾部斜杠 / 是可选。/hello-pages 和 /hello-pages/ 将被路由到 /node-functions/hello-pages.js。 如果没有匹配到 Node Functions 路由,客户端请求将被路由到 Pages 对应的静态资源。 路由大小写敏感,/helloworld 将被路由到 /node-functions/helloworld.js,不能被路由到 /node-functions/HelloWorld.js
Node Functions 支持动态路由,上述示例中一级动态路径 /node-functions/api/users/[id].js,多级动态路径 /node-functions/api/[[default]].js。参考下述用法: |文件路径|路由|匹配| |/node-functions/api/users/[id].js|example.com/api/users/1024|是| |/node-functions/api/users/[id].js|example.com/api/users/vip/1024|否| |/node-functions/api/users/[id].js|example.com/api/vip/1024|否| |/node-functions/api/[[default]].js|example.com/api/books/list|是| |/node-functions/api/[[default]].js|example.com/api/1024|是| |/node-functions/api/[[default]].js|example.com/v2/vip/1024|否|
框架内置路由 在涉及到 Express/Koa 框架的开发时,代码的编写与文件组织形式有几个要点需要注意。
Express/Koa 框架: 所有路由服务都收拢在一个函数文件内,且文件名必须是 [[]] 的格式,如 [[default]].js。 无需额外启动 HTTP Server 与设置端口监听 必须导出框架实例否则构建器不会将其识别为函数 export default app; 例如,在目录 node-functions/express/* 下创建 express 应用,需以 [[default]].js 作为入口,且所有 express 相关路由都在 [[default]].js 里面:
node-functions
└── express
└── [[default]].js # express.js/koa.js 入口文件,包含框架内置路由定义
Function Handlers 使用 Functions Handlers 可为 Pages 创建自定义请求处理程序,以及定义 RESTful API 实现全栈应用。支持下述的 Handlers 方法:
| Handlers 方法 | 描述 |
|---|---|
| onRequest(context: EventContext): Response | Promise | 匹配 HTTP Methods |
| onRequestGet(context: EventContext): Response | Promise | 匹配 HTTP Methods (GET) |
| onRequestPost(context: EventContext): Response | Promise | 匹配 HTTP Methods (POST) |
| onRequestPatch(context: EventContext): Response | Promise | 匹配 HTTP Methods (PATCH) |
| onRequestPut(context: EventContext): Response | Promise | 匹配 HTTP Methods (PUT) |
| onRequestDelete(context: EventContext): Response | Promise | 匹配 HTTP Methods (DELETE) |
| onRequestHead(context: EventContext): Response | Promise | 匹配 HTTP Methods (HEAD) |
| onRequestOptions(context: EventContext): Response | Promise | 匹配 HTTP Methods (OPTIONS) |
context 是传递给 Function Handlers 方法的对象,包含下述属性:
- uuid:EO-LOG-UUID 代表了 EO 请求的唯一标识符
- params:动态路由 /node-functions/api/users/[id].js 参数值
export function onRequestGet(context) {
return new Response(`User id is ${context.params.id}`);
}- env:Pages 环境变量
- clientIp:客户端 IP 地址
- server:
- region: 部署地的区域编码
- requestId: 请求 ID,用于日志跟踪
- geo:客户端地理位置信息
函数必须返回一个 Response 对象,来响应客户端的 HTTP 请求,包含 headers,status,body 等属性。
除了标准的 HTTP 请求外,Node Functions 还支持处理和响应 WebSocket 升级请求,以建立持久化的双向通信连接。 当函数接收到包含 Upgrade: websocket 头信息的 HTTP GET 请求时,您可以通过平台提供的能力将该连接升级为 WebSocket。
| 内容 | 限制 | 说明 |
|---|---|---|
| 代码包大小 | 128 MB | 单个函数代码包大小最多支持 128 MB |
| 请求 body 大小 | 6 MB | 客户端请求携带 body 最多支持 6 MB |
| 单次运行时长 | 120s | 函数单次运行时长最多支持 120 秒 |
| 墙上时间 | 120s | 函数单次运行墙上时间最多支持 120 秒 |
| 开发语言 | Node | 目前仅支持 Node.js,默认版本 v20.x |
注意: 涉及到文件传输时,不建议存储需要长期保留的数据。
除了通过控制台进行项目设置,您还可以在项目根目录下创建 edgeone.json 文件,来定义和覆盖项目的默认行为,以便更灵活地配置项目。
配置文件包括以下设置项:
覆盖 控制台 - 项目设置- 构建部署配置 中的构建命令。
{
"buildCommand": "next build"
}覆盖 控制台 - 项目设置- 构建部署配置 中的安装命令。此配置项可自定义构建过程使用的包管理器。
{
"installCommand": "npm install"
}覆盖 控制台 - 项目设置- 构建部署配置 中的输出目录。
{
"outputDirectory": "./build"
}指定构建环境的 node 版本。建议使用系统预装的 14.21.3、16.20.2、18.20.4、20.18.0、22.11.0 几个版本,若填写其他版本号,可能导致部署失败。
{
"nodeVersion": "22.11.0"
}将请求从一个 URL 重定向到另一个 URL,以下是几个重定向的示例。
使用 301 永久重定向将请求从 /articles/+ ID 的 URL(例如 /articles/123),重定向到 /news-articles/ + ID 的 URL(例如 /news-articles/123):
{
"redirects": [
{
"source": "/articles/:id",
"destination": "/news-articles/:id",
"statusCode": 301
}
]
}使用 302 临时重定向将请求从 /old-path ,重定向到 /new-path :
{
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"statusCode": 302
}
]
}使用 301 永久重定向将请求从 /template-source ,重定向到外部站点的绝对路径 https://github.com/TencentEdgeOne/pages-templates/tree/main/examples/chrome-ai :
{
"redirects": [
{
"source": "/template-source",
"destination": "https://github.com/TencentEdgeOne/pages-templates/tree/main/examples/chrome-ai",
"statusCode": 301
}
]
}使用 301 永久重定向将非 www 请求重定向到 www,同时也支持反向重定向(仅对自定义域名生效):
{
"redirects": [
{
"source": "$host",
"destination": "$wwwhost",
"statusCode": 301
}
]
}注意: redirect 最大数量限制为 100 个 source 与 destination 不超过 500 个字符
将所有以 /assets/ 开头的请求重写到 /assets-new/ 目录下,同时保留原始请求的路径部分。
{
"rewrites": [
{
"source": "/assets/*",
"destination": "/assets-new/:splat"
}
]
}进一步细化重写规则,专门针对 PNG 格式的图像文件进行处理。以下示例将确保所有以 .png 结尾的请求都被重写到新的路径,同时保留文件名。
{
"rewrites": [
{
"source": "/assets/images/*.png",
"destination": "/assets-new/images/:splat"
}
]
}-
注意:
-
rewrite 最大数量限制为 100 个
-
source 与 destination 不超过 500 个字符
-
该配置仅适用于静态资源访问
-
不支持 SPA (单页应用) 的前端路由重写
-
源路径必须以
/开头 -
SPA 应用重写建议
-
如需在 SPA 中实现 URL 重写,建议采用以下方案:
-
前端路由重定向
- 使用框架自带的路由系统进行路径重定向
- 在路由配置中定义重写规则
自定义和管理 HTTP 响应头,以改善网站的性能和安全性,同时提升用户体验。
通过为所有请求设置 X-Frame-Options 头部来增强网站的安全性,防止点击劫持攻击;同时,通过 Cache-Control 指定响应可以被缓存 2 小时,以提高性能和减少服务器负担。
{
"headers": [
{
"source": "/*",
"headers": [
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "Cache-Control",
"value": "max-age=7200"
}
]
}
]
}
进一步优化特定资源的缓存策略,针对 /assets/ 目录下的静态资源进行处理。此示例将为该目录下的所有文件设置更长的缓存时间,例如 1 年。
```json
{
"headers": [
{
"source": "/assets/*",
"headers": [
{
"key": "Cache-Control",
"value": "s-maxage=10000, max-age=31536000"
},
{
"key": "Pages-Cache-Control",
"value": "s-maxage=10000"
}
]
}
]
}注意: header 最大数量限制为 30 个 每个 header 的 key 限制为 1 - 100 个字符,允许使用数字、字母及特殊符号 '-' 每个 header 的 value 限制为 1 - 1000 个字符,不支持中文
针对不同资源配置边缘缓存时间,优化不同资源的边缘缓存策略,提升请求资源的加载速度。
将 images 目录下所有文件资源设置为缓存 1 天。
{
"caches": [
{
"source": "/images/*",
"cacheTtl": 86400
}
]
}
针对特定文件设置缓存,此示例将 sitemap.xml 文件设置为不缓存, 将 images 下所有 jpg 文件设置为缓存 1 小时。
{
"caches": [
{
"source": "/sitemap.xml",
"cacheTtl": 0
},
{
"source": "/images/*.jpg",
"cacheTtl": 3600
}
]
}注意: cacheTtl 以秒为单位,不允许小数且不能小于 0,设置为 0 时不缓存
在配置 redirects、rewrites、headers 和 caches 时,source 字段用于定义请求路径的匹配规则。以下是主要的匹配特性:
1.路径匹配 source 支持使用特定的模式来匹配请求路径。匹配规则会根据请求的 URL 进行解析。
2.通配符 使用星号(*)作为通配符,可以匹配路径中的任意字符。请注意,source 中只能包含一个通配符。
3.占位符 占位符以冒号(:)开头,后跟占位符名称。每个占位符只能在 source 中使用一次,并且会匹配除分隔符外的所有字符。
included_files:如果您的 node 函数需要直接读取文件,需要配置included_files列表,构建器会把这些文件复制到构建产物让函数在部署后能够正确读取到这些文件。路径的格式是相对于项目根目录的相对路径(不要以./或/开头),支持 glob 模式。
{
"node-functions": {
"included_files": [
"assets/**",
"assets2/**/*.json", // glob 模式
"public/font-example.ttf", // 精确匹配
"assets/**", // 包含 assets 目录下所有文件
"!assets/**/*.tmp", // 但排除所有 .tmp 文件
"assets/images/**/*.{png,jpg,jpeg,gif,webp}", // 图片资源
]
}
}目前仅支持在函数代码中使用相对路径引入文件:
import { readFileSync } from 'fs';
export function onRequest() {
const image = readFileSync('../../assets/your-file.png');
return new Response(image);
}- external_node_modules:有一些依赖包包含原生模块或者静态文件,需要配置external_node_modules,让构建器能够正确分离出这些依赖并复制到构建产物中。
{
"node-functions": {
"external_node_modules": [
"svg-captcha"
]
}
}Node Functions 的最大单次运行时长限制,可配置范围为 10 - 120 秒,不配置时默认 30 秒。
{
"nodeFunctionsConfig": {
"maxDuration": 10
}
}如下示例展示了如何将多个设置组合在一个配置文件中,包括但不限于所有可用选项。请注意,文件中的每个设置项并非必需。
{
"name": "example-app",
"buildCommand": "next build",
"installCommand": "npm install",
"outputDirectory": "./build",
"nodeVersion": "22.11.0",
"redirects": [
{
"source": "/articles/:id",
"destination": "/news-articles/:id",
"statusCode": 301
},
{
"source": "/old-path",
"destination": "/new-path",
"statusCode": 302
}
],
"rewrites": [
{
"source": "/assets/*",
"destination": "/assets-new/:splat"
}
],
"headers": [
{
"source": "/*",
"headers": [
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "Cache-Control",
"value": "max-age=7200"
}
]
},
{
"source": "/assets/*",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000"
}
]
}
],
"node-functions": {
"external_node_modules": [
"svg-captcha"
],
"included_files": [
"assets/**",
]
}
}