Cloudflare Pages Functions 实战指南:基于文件路由的 Cloudflare Pages 全栈开发
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
本文是 cloudflare-deploy skill 中 Pages Functions 专题的深度展开。Cloudflare Pages Functions 让你在 Pages 静态站点之上直接编写基于 Workers 运行时的无服务器函数,通过文件系统即路由(file-based routing)快速搭建全栈应用。读完本文,你将掌握 Pages Functions 的选型决策、文件路由与动态路由语法、EventContext 与各类 Handler 的用法、KV/D1/R2 等绑定配置,以及中间件、认证、限流、调试和部署的完整实战方案。
Pages Functions 是什么
Cloudflare Pages Functions 是在 Cloudflare Pages 平台上运行的无服务器函数,底层基于 Workers 运行时。它的核心设计是文件即路由:在项目根目录放置一个functions/目录,其中的每个文件自动映射为一个 HTTP 路由,无需额外配置即可让静态站点拥有动态后端能力。
Pages Functions 面向"静态站点 + 动态能力"的组合场景,支持方法级处理器(onRequestGet、onRequestPost等)、_middleware.js中间件,以及 KV、D1、R2、Durable Objects、Workers AI、Service Bindings 等全套绑定能力。作为参考,本 skill 的 Product Index 将 Pages Functions 归类在 Compute & Runtime 产品族中,与 Workers、Pages、Durable Objects 并列。
选型决策树:什么时候用 Pages Functions
在动手之前,先根据需求判断是否应该选择 Pages Functions。以下决策树来自 README.md:
Need serverless backend? ├─ Yes, for a static site → Pages Functions ├─ Yes, standalone API → Workers └─ Just static hosting → Pages (no functions) Have existing Worker? ├─ Complex routing logic → Use _worker.js (Advanced Mode) └─ Simple routes → Migrate to /functions (File-Based) Framework-based? ├─ Next.js/SvelteKit/Remix → Uses _worker.js automatically └─ Vanilla/HTML/React SPA → Use /functions要点解读:
- 纯静态托管:不需要后端,直接用 Pages 即可,无需引入 Functions;
- 静态站点需要后端逻辑:如表单处理、鉴权、数据读写,选 Pages Functions;
- 独立 API 服务:更偏向直接使用 Workers;
- 已有 Worker 且路由逻辑复杂:可改用
_worker.js高级模式(Advanced Mode),自己掌控完整 fetch 流程; - 使用框架:Next.js、SvelteKit、Remix 等框架会自动生成
_worker.js,你通常不需要手动管理functions/目录;原生 HTML/React SPA 则适合文件式路由。
文件式路由(File-Based Routing)
Pages Functions 的核心机制是文件路径到 URL 的映射。在项目根目录创建functions/目录:
/functions ├── index.js → / ├── api.js → /api ├── users/ │ ├── index.js → /users/ │ ├── [user].js → /users/:user │ └── [[catchall]].js → /users/* └── _middleware.js → runs on all routes路由规则:
index.js对应目录根路径;- 结尾斜杠可省略(
/users/与/users等价); - 具体路由优先于 catch-all 路由;
- 若没有函数匹配,则回退到静态资源。
动态路由(Dynamic Routes)
Pages Functions 支持单段与多段两种动态路由语法。
单段参数[param]→ 字符串:匹配单个路径段,参数通过context.params以字符串形式暴露:
// /functions/users/[user].js export function onRequest(context) { return new Response(`Hello ${context.params.user}`); } // Matches: /users/nevi多段参数[[param]]→ 数组:匹配零个或多个路径段,参数以数组形式暴露:
// /functions/users/[[catchall]].js export function onRequest(context) { return new Response(JSON.stringify(context.params.catchall)); } // Matches: /users/nevi/foobar → ["nevi", "foobar"]注意[param](单中括号)与[[param]](双中括号)的语义差异:前者匹配单个段,后者匹配多段路径。
函数 API:EventContext 与 Handler
每个函数文件导出一个或多个 handler。handler 接收统一的EventContext对象,其完整结构定义见 api.md:
interface EventContext<Env = any> { request: Request; // Incoming request functionPath: string; // Request path waitUntil(promise: Promise<any>): void; // Background tasks (non-blocking) passThroughOnException(): void; // Fallback to static on error next(input?: Request | string, init?: RequestInit): Promise<Response>; env: Env; // Bindings, vars, secrets params: Record<string, string | string[]>; // Route params ([user] or [[catchall]]) data: any; // Middleware shared state }各字段职责:
request:当前请求对象,读取 headers、body、URL 等;params:路由参数,动态路由一节中[user]/[[catchall]]的取值就在这里;env:绑定的命名空间、环境变量与密钥的入口;next():调用链中的下一个处理器(中间件核心);waitUntil():注册后台任务,不阻塞响应;data:中间件之间共享状态的通道;passThroughOnException():函数抛异常时回退到静态资源。
通用与按方法 Handler
// Generic (fallback for any method) export async function onRequest(ctx: EventContext): Promise<Response> { return new Response('Any method'); } // Method-specific (takes precedence over generic) export async function onRequestGet(ctx: EventContext): Promise<Response> { return Response.json({ message: 'GET' }); } export async function onRequestPost(ctx: EventContext): Promise<Response> { const body = await ctx.request.json(); return Response.json({ received: body }); } // Also: onRequestPut, onRequestPatch, onRequestDelete, onRequestHead, onRequestOptions通用onRequest作为任意方法的兜底,而onRequestGet、onRequestPost等按方法命名的 handler 优先级更高。需要处理 JSON 请求体时,直接await ctx.request.json()即可。
绑定(Bindings)配置与使用
绑定把 Cloudflare 平台的存储、计算与 AI 能力注入到ctx.env中。api.md 给出了完整对照表:
| Binding Type | Interface | Config Key | Use Case |
|---|---|---|---|
| KV | KVNamespace | kv_namespaces | Key-value cache, sessions, config |
| D1 | D1Database | d1_databases | Relational data, SQL queries |
| R2 | R2Bucket | r2_buckets | Large files, user uploads, assets |
| Durable Objects | DurableObjectNamespace | durable_objects.bindings | Stateful coordination, websockets |
| Workers AI | Ai | ai.binding | LLM inference, embeddings |
| Vectorize | VectorizeIndex | vectorize | Vector search, embeddings |
| Service Binding | Fetcher | services | Worker-to-worker RPC |
| Analytics Engine | AnalyticsEngineDataset | analytics_engine_datasets | Event logging, metrics |
| Environment Vars | string | vars | Non-sensitive config |
KV:键值缓存与会话
interface Env { KV: KVNamespace; } export const onRequest: PagesFunction<Env> = async (ctx) => { await ctx.env.KV.put('key', 'value', { expirationTtl: 3600 }); const val = await ctx.env.KV.get('key', { type: 'json' }); const keys = await ctx.env.KV.list({ prefix: 'user:' }); return Response.json({ val }); };适合存配置、会话与缓存;expirationTtl控制过期时间(秒),type: 'json'可自动反序列化。
D1:关系型 SQL
interface Env { DB: D1Database; } export const onRequest: PagesFunction<Env> = async (ctx) => { const user = await ctx.env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(123).first(); return Response.json(user); };D1 提供 SQLite 兼容的关系型查询,prepare(...).bind(...)做参数绑定避免注入风险。
R2:对象存储
interface Env { BUCKET: R2Bucket; } export const onRequest: PagesFunction<Env> = async (ctx) => { const obj = await ctx.env.BUCKET.get('file.txt'); if (!obj) return new Response('Not found', { status: 404 }); await ctx.env.BUCKET.put('file.txt', ctx.request.body); return new Response(obj.body); };适合大文件、用户上传与静态资产,S3 兼容。
Durable Objects:有状态协调
interface Env { COUNTER: DurableObjectNamespace; } export const onRequest: PagesFunction<Env> = async (ctx) => { const stub = ctx.env.COUNTER.get(ctx.env.COUNTER.idFromName('global')); return stub.fetch(ctx.request); };idFromName('global')按名字取稳定实例,stub.fetch()把请求转发给 DO 实例处理。
Workers AI:LLM 推理
interface Env { AI: Ai; } export const onRequest: PagesFunction<Env> = async (ctx) => { const resp = await ctx.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { prompt: 'Hello' }); return Response.json(resp); };通过ai.binding配置,直接调用 Workers AI 的模型完成推理。
Service Bindings 与环境变量
interface Env { AUTH: Fetcher; API_KEY: string; } export const onRequest: PagesFunction<Env> = async (ctx) => { // Service binding: forward to another Worker return ctx.env.AUTH.fetch(ctx.request); // Environment variable return Response.json({ key: ctx.env.API_KEY }); };Service Binding 实现 Worker 到 Worker 的 RPC;vars中定义的非敏感配置直接以字符串读取。
TypeScript 与 wrangler.jsonc 配置
configuration.md 详细说明了类型与配置体系。
TypeScript 设置
推荐用wrangler types从wrangler.jsonc自动生成类型(取代已弃用的@cloudflare/workers-types):
npx wrangler types命令会生成worker-configuration.d.ts,其中包含基于绑定定义的类型化Env接口:
// functions/api.ts export const onRequest: PagesFunction<Env> = async (ctx) => { // ctx.env.KV, ctx.env.DB, etc. are fully typed return Response.json({ ok: true }); };若不使用wrangler types,也可手动声明Env接口:
interface Env { KV: KVNamespace; DB: D1Database; API_KEY: string; } export const onRequest: PagesFunction<Env> = async (ctx) => { /* ... */ };wrangler.jsonc 完整示例
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "my-pages-app", "pages_build_output_dir": "./dist", "compatibility_date": "2025-01-01", "compatibility_flags": ["nodejs_compat"], "vars": { "API_URL": "https://api.example.com" }, "kv_namespaces": [{ "binding": "KV", "id": "abc123" }], "d1_databases": [{ "binding": "DB", "database_name": "prod-db", "database_id": "xyz789" }], "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "my-bucket" }], "durable_objects": { "bindings": [{ "name": "COUNTER", "class_name": "Counter", "script_name": "counter-worker" }] }, "services": [{ "binding": "AUTH", "service": "auth-worker" }], "ai": { "binding": "AI" }, "vectorize": [{ "binding": "VECTORIZE", "index_name": "my-index" }], "analytics_engine_datasets": [{ "binding": "ANALYTICS" }] }关键字段说明:
pages_build_output_dir:构建产物目录,Pages Functions 就在此输出中被识别;compatibility_date/compatibility_flags:指定运行时兼容日期与特性开关,如nodejs_compat可启用 Node.js 兼容 API;vars:非敏感环境变量;kv_namespaces、d1_databases、r2_buckets等:各绑定命名空间,binding字段必须与代码中ctx.env的键名完全一致(大小写敏感)。
环境覆盖(Environment Overrides)
配置遵循"顶层 → 本地开发、env.preview→ 预览、env.production→ 生产"的覆盖层级:
{ "vars": { "API_URL": "http://localhost:8787" }, "env": { "production": { "vars": { "API_URL": "https://api.example.com" } } } }注意:一旦在某环境里覆盖vars、kv_namespaces、d1_databases等数组/对象字段,必须把其中所有项在该环境内完整重新定义——这些配置不可继承。
本地密钥(.dev.vars)
.dev.vars仅用于本地开发,不会被部署到线上:
# .dev.vars (add to .gitignore) SECRET_KEY="my-secret-value"本地通过ctx.env.SECRET_KEY读取。生产环境的密钥需要用wrangler pages secret put单独设置:
echo "value" | npx wrangler pages secret put SECRET_KEY --project-name=my-app静态配置文件
Pages 支持三类静态配置文件(见 configuration.md):
_routes.json—— 自定义路由包含/排除规则:
{ "version": 1, "include": ["/api/*"], "exclude": ["/static/*"] }_headers—— 静态资源响应头:
/static/* Cache-Control: public, max-age=31536000_redirects—— 路径重定向:
/old /new 301常见模式:中间件、认证、限流与后台任务
patterns.md 汇总了高频率实战模式。
中间件与认证
functions/_middleware.js作用于全局所有路由,functions/users/_middleware.js则只作用于users路由树。中间件通过ctx.next()把请求交给后续处理器:
// functions/_middleware.js (global) or functions/users/_middleware.js (scoped) export async function onRequest(ctx) { try { return await ctx.next(); } catch (err) { return new Response(err.message, { status: 500 }); } } // Chained: export const onRequest = [errorHandler, auth, logger];认证中间件——校验 Bearer Token,把用户信息写入ctx.data供后续共享:
async function auth(ctx: EventContext<Env>) { const token = ctx.request.headers.get('authorization')?.replace('Bearer ', ''); if (!token) return new Response('Unauthorized', { status: 401 }); const session = await ctx.env.KV.get(`session:${token}`); if (!session) return new Response('Invalid', { status: 401 }); ctx.data.user = JSON.parse(session); return ctx.next(); }CORS 与基于 KV 的限流
// CORS middleware const cors = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' }; export async function onRequestOptions() { return new Response(null, { headers: cors }); } export async function onRequest(ctx) { const res = await ctx.next(); Object.entries(cors).forEach(([k, v]) => res.headers.set(k, v)); return res; } // Rate limiting (KV-based) async function rateLimit(ctx: EventContext<Env>) { const ip = ctx.request.headers.get('CF-Connecting-IP') || 'unknown'; const count = parseInt(await ctx.env.KV.get(`rate:${ip}`) || '0'); if (count >= 100) return new Response('Rate limited', { status: 429 }); await ctx.env.KV.put(`rate:${ip}`, (count + 1).toString(), { expirationTtl: 3600 }); return ctx.next(); }限流思路:以CF-Connecting-IP作为客户端标识,在 KV 中维护计数,超过阈值返回 429,expirationTtl: 3600让计数每小时自动过期。
表单、缓存与重定向
// JSON & file upload export async function onRequestPost(ctx) { const ct = ctx.request.headers.get('content-type') || ''; if (ct.includes('application/json')) return Response.json(await ctx.request.json()); if (ct.includes('multipart/form-data')) { const file = (await ctx.request.formData()).get('file') as File; await ctx.env.BUCKET.put(file.name, file.stream()); return Response.json({ uploaded: file.name }); } } // Cache API export async function onRequest(ctx) { let res = await caches.default.match(ctx.request); if (!res) { res = new Response('Data'); res.headers.set('Cache-Control', 'public, max-age=3600'); ctx.waitUntil(caches.default.put(ctx.request, res.clone())); } return res; } // Redirects export async function onRequest(ctx) { if (new URL(ctx.request.url).pathname === '/old') { return Response.redirect(new URL('/new', ctx.request.url), 301); } return ctx.next(); }后台任务(waitUntil)
waitUntil注册的 Promise 在响应返回后继续执行,适合埋点、清理、Webhook 通知等非阻塞任务:
export async function onRequest(ctx: EventContext<Env>) { const res = Response.json({ success: true }); ctx.waitUntil(ctx.env.KV.put('last-visit', new Date().toISOString())); ctx.waitUntil(Promise.all([ ctx.env.ANALYTICS.writeDataPoint({ event: 'view' }), fetch('https://webhook.site/...', { method: 'POST' }) ])); return res; // Returned immediately }单元测试
用 Vitest +cloudflare:test直接对 handler 做单元测试:
import { env } from 'cloudflare:test'; import { it, expect } from 'vitest'; import { onRequest } from '../functions/api'; it('returns JSON', async () => { const req = new Request('http://localhost/api'); const ctx = { request: req, env, params: {}, data: {} } as EventContext; const res = await onRequest(ctx); expect(res.status).toBe(200); });集成测试则用wrangler pages dev起本地服务,配合 Playwright/Cypress 做端到端验证。
高级模式(Advanced Mode:_worker.js)
当路由逻辑复杂、或项目由框架生成(Next.js/SvelteKit/Remix)时,可放弃functions/目录改用_worker.js,获得完整的 Worker 形态控制权。此时静态资源通过env.ASSETS.fetch()访问(api.md):
interface Env { ASSETS: Fetcher; KV: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); if (url.pathname.startsWith('/api/')) { return Response.json({ data: await env.KV.get('key') }); } return env.ASSETS.fetch(request); // Fallback to static } } satisfies ExportedHandler<Env>;何时使用高级模式:已有 Worker、框架自动生成(Next.js/SvelteKit)、需要自定义路由逻辑。
本地开发与部署
来自 configuration.md 的完整命令:
# Dev server npx wrangler pages dev ./dist # With bindings npx wrangler pages dev ./dist --kv=KV --d1=DB=db-id --r2=BUCKET # Durable Objects (2 terminals) cd do-worker && npx wrangler dev cd pages-project && npx wrangler pages dev ./dist --do COUNTER=Counter@do-worker # Deploy npx wrangler pages deploy ./dist npx wrangler pages deploy ./dist --branch preview # Download config npx wrangler pages download config my-project要点:
- 开发时先构建静态站点到
pages_build_output_dir指定的目录(如./dist)再pages dev; - 本地联调绑定可通过
--kv、--d1、--r2、--do等参数挂载; - 部署支持
--branch指定预览分支; - 部署前务必确认已认证(参考 SKILL.md 中的
npx wrangler whoami检查),生产密钥使用wrangler pages secret put设置。
错误诊断与调试
gotchas.md 给出了高频问题速查表:
| Symptom | Likely Cause | Solution |
|---|---|---|
| Function not invoking | Wrong/functionslocation, wrong extension, or_routes.jsonexcludes path | Checkpages_build_output_dir, use.js/.ts, verify_routes.json |
ctx.env.BINDINGundefined | Binding not configured or name mismatch | Add towrangler.jsonc, verify exact name (case-sensitive), redeploy |
TypeScript errors onctx.env | Missing type definition | Runwrangler typesor defineinterface Env {} |
| Middleware not running | Wrong filename/location or missingctx.next() | Name exactly_middleware.js, exportonRequest, callctx.next() |
| Secrets missing in production | .dev.varsnot deployed | .dev.varsis local only - set production secrets via dashboard orwrangler secret put |
| Type mismatch on binding | Wrong interface type | See bindings table for correct types |
| "KV key not found" but exists | Key in wrong namespace or env | Verify namespace binding, check preview vs production env |
| Function times out | Synchronous wait or missingawait | All I/O must be async/await, usectx.waitUntil()for background tasks |
调试手段包括:
// Console logging export async function onRequest(ctx) { console.log('Request:', ctx.request.method, ctx.request.url); const res = await ctx.next(); console.log('Status:', res.status); return res; }# Stream real-time logs npx wrangler pages deployment tail npx wrangler pages deployment tail --status error// Source maps (wrangler.jsonc) { "upload_source_maps": true }平台限制
| Resource | Free | Paid |
|---|---|---|
| CPU time | 10ms | 50ms |
| Memory | 128 MB | 128 MB |
| Script size | 10 MB compressed | 10 MB compressed |
| Env vars | 5 KB per var, 64 max | 5 KB per var, 64 max |
| Requests | 100k/day | Unlimited ($0.50/million) |
从限制表可以推断:函数内所有 I/O 必须保持异步(async/await),避免同步阻塞导致 CPU 时间耗尽;脚本体积应控制在压缩后 10MB 以内,因此要精简依赖以降低冷启动时间。
最佳实践
性能:最小化依赖(减小冷启动)、按用途选存储(KV 做缓存、D1 做关系型、R2 存大文件)、设置Cache-Control头、批量数据库操作、优雅处理错误。
安全:绝不提交密钥(用.dev.vars+ gitignore)、校验输入、写入数据库前做清洗、实现认证中间件、设置 CORS 头、按 IP 限流。
迁移指南
Workers → Pages Functions:
export default { fetch(req, env) {} }→export function onRequest(ctx) { const { request, env } = ctx; };- 复杂路由改用
_worker.js,静态文件通过env.ASSETS.fetch(request)访问。
其他平台 → Pages:
- 文件式路由:
/functions/api/users.js对应/api/users; - 动态路由用
[param]而非:param; - 用 Workers API 替代 Node.js 依赖,或添加
nodejs_compat兼容标志。
阅读顺序与延伸参考
如果你初次接触 Pages Functions,推荐按以下顺序阅读本 skill 内的专题文档:
- pages-functions/README.md —— 概览、路由、选型决策树;
- configuration.md —— TypeScript 设置、wrangler.jsonc、绑定配置;
- api.md —— EventContext、Handler、绑定参考;
- patterns.md —— 中间件、认证、CORS、限流、缓存;
- gotchas.md —— 常见错误、调试、限制。
需要速查时:绑定对照表看 api.md,错误诊断看 gotchas.md,TypeScript 设置看 configuration.md。
此外,Pages 平台总览、Workers 运行时 API 与 D1 数据库集成 也是与本主题直接相关的延伸资料,可结合阅读。
【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考