Cloudflare Pages Functions 实战指南:基于文件路由的 Cloudflare Pages 全栈开发 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.mdNeed 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 RoutingPages 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 RoutesPages 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]]双中括号的语义差异前者匹配单个段后者匹配多段路径。函数 APIEventContext 与 Handler每个函数文件导出一个或多个 handler。handler 接收统一的EventContext对象其完整结构定义见 api.mdinterface EventContextEnv any { request: Request; // Incoming request functionPath: string; // Request path waitUntil(promise: Promiseany): void; // Background tasks (non-blocking) passThroughOnException(): void; // Fallback to static on error next(input?: Request | string, init?: RequestInit): PromiseResponse; env: Env; // Bindings, vars, secrets params: Recordstring, 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): PromiseResponse { return new Response(Any method); } // Method-specific (takes precedence over generic) export async function onRequestGet(ctx: EventContext): PromiseResponse { return Response.json({ message: GET }); } export async function onRequestPost(ctx: EventContext): PromiseResponse { 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 TypeInterfaceConfig KeyUse CaseKVKVNamespacekv_namespacesKey-value cache, sessions, configD1D1Databased1_databasesRelational data, SQL queriesR2R2Bucketr2_bucketsLarge files, user uploads, assetsDurable ObjectsDurableObjectNamespacedurable_objects.bindingsStateful coordination, websocketsWorkers AIAiai.bindingLLM inference, embeddingsVectorizeVectorizeIndexvectorizeVector search, embeddingsService BindingFetcherservicesWorker-to-worker RPCAnalytics EngineAnalyticsEngineDatasetanalytics_engine_datasetsEvent logging, metricsEnvironment VarsstringvarsNon-sensitive configKV键值缓存与会话interface Env { KV: KVNamespace; } export const onRequest: PagesFunctionEnv 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关系型 SQLinterface Env { DB: D1Database; } export const onRequest: PagesFunctionEnv 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: PagesFunctionEnv 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: PagesFunctionEnv async (ctx) { const stub ctx.env.COUNTER.get(ctx.env.COUNTER.idFromName(global)); return stub.fetch(ctx.request); };idFromName(global)按名字取稳定实例stub.fetch()把请求转发给 DO 实例处理。Workers AILLM 推理interface Env { AI: Ai; } export const onRequest: PagesFunctionEnv 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: PagesFunctionEnv 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 的 RPCvars中定义的非敏感配置直接以字符串读取。TypeScript 与 wrangler.jsonc 配置configuration.md 详细说明了类型与配置体系。TypeScript 设置推荐用wrangler types从wrangler.jsonc自动生成类型取代已弃用的cloudflare/workers-typesnpx wrangler types命令会生成worker-configuration.d.ts其中包含基于绑定定义的类型化Env接口// functions/api.ts export const onRequest: PagesFunctionEnv 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: PagesFunctionEnv 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 兼容 APIvars非敏感环境变量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_KEYmy-secret-value本地通过ctx.env.SECRET_KEY读取。生产环境的密钥需要用wrangler pages secret put单独设置echo value | npx wrangler pages secret put SECRET_KEY --project-namemy-app静态配置文件Pages 支持三类静态配置文件见 configuration.md_routes.json—— 自定义路由包含/排除规则{ version: 1, include: [/api/*], exclude: [/static/*] }_headers—— 静态资源响应头/static/* Cache-Control: public, max-age31536000_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: EventContextEnv) { 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: EventContextEnv) { 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 中维护计数超过阈值返回 429expirationTtl: 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-age3600); 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(); }后台任务waitUntilwaitUntil注册的 Promise 在响应返回后继续执行适合埋点、清理、Webhook 通知等非阻塞任务export async function onRequest(ctx: EventContextEnv) { 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.mdinterface Env { ASSETS: Fetcher; KV: KVNamespace; } export default { async fetch(request: Request, env: Env): PromiseResponse { 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 ExportedHandlerEnv;何时使用高级模式已有 Worker、框架自动生成Next.js/SvelteKit、需要自定义路由逻辑。本地开发与部署来自 configuration.md 的完整命令# Dev server npx wrangler pages dev ./dist # With bindings npx wrangler pages dev ./dist --kvKV --d1DBdb-id --r2BUCKET # Durable Objects (2 terminals) cd do-worker npx wrangler dev cd pages-project npx wrangler pages dev ./dist --do COUNTERCounterdo-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 给出了高频问题速查表SymptomLikely CauseSolutionFunction not invokingWrong/functionslocation, wrong extension, or_routes.jsonexcludes pathCheckpages_build_output_dir, use.js/.ts, verify_routes.jsonctx.env.BINDINGundefinedBinding not configured or name mismatchAdd towrangler.jsonc, verify exact name (case-sensitive), redeployTypeScript errors onctx.envMissing type definitionRunwrangler typesor defineinterface Env {}Middleware not runningWrong 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 putType mismatch on bindingWrong interface typeSee bindings table for correct typesKV key not found but existsKey in wrong namespace or envVerify namespace binding, check preview vs production envFunction times outSynchronous wait or missingawaitAll 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 }平台限制ResourceFreePaidCPU time10ms50msMemory128 MB128 MBScript size10 MB compressed10 MB compressedEnv vars5 KB per var, 64 max5 KB per var, 64 maxRequests100k/dayUnlimited ($0.50/million)从限制表可以推断函数内所有 I/O 必须保持异步async/await避免同步阻塞导致 CPU 时间耗尽脚本体积应控制在压缩后 10MB 以内因此要精简依赖以降低冷启动时间。最佳实践性能最小化依赖减小冷启动、按用途选存储KV 做缓存、D1 做关系型、R2 存大文件、设置Cache-Control头、批量数据库操作、优雅处理错误。安全绝不提交密钥用.dev.vars gitignore、校验输入、写入数据库前做清洗、实现认证中间件、设置 CORS 头、按 IP 限流。迁移指南Workers → Pages Functionsexport 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.mdTypeScript 设置看 configuration.md。此外Pages 平台总览、Workers 运行时 API 与 D1 数据库集成 也是与本主题直接相关的延伸资料可结合阅读。【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考