diff --git a/.agents/services/hosting/cloudflare-platform-skill/pages-functions-patterns.md b/.agents/services/hosting/cloudflare-platform-skill/pages-functions-patterns.md index 2a3f00896f..7025e92856 100644 --- a/.agents/services/hosting/cloudflare-platform-skill/pages-functions-patterns.md +++ b/.agents/services/hosting/cloudflare-platform-skill/pages-functions-patterns.md @@ -4,14 +4,9 @@ ```javascript // functions/_middleware.js (global) or functions/users/_middleware.js (scoped to /users/*) - -// Single export async function onRequest(context) { - try { - return await context.next(); - } catch (err) { - return new Response(`${err.message}\n${err.stack}`, { status: 500 }); - } + try { return await context.next(); } + catch (err) { return new Response(`${err.message}\n${err.stack}`, { status: 500 }); } } // Chained (runs in array order) @@ -24,14 +19,11 @@ export const onRequest = [errorHandling, authentication, logging]; async function authMiddleware(context: EventContext) { const token = context.request.headers.get('authorization')?.replace('Bearer ', ''); if (!token) return new Response('Unauthorized', { status: 401 }); - const session = await context.env.KV.get(`session:${token}`); if (!session) return new Response('Invalid token', { status: 401 }); - context.data.user = JSON.parse(session); return context.next(); } - export const onRequest = [authMiddleware]; ``` @@ -44,11 +36,9 @@ const corsHeaders = { 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400', }; - -export async function onRequestOptions(context) { +export async function onRequestOptions() { return new Response(null, { headers: corsHeaders }); } - export async function onRequest(context) { const response = await context.next(); Object.entries(corsHeaders).forEach(([k, v]) => response.headers.set(k, v)); @@ -60,14 +50,11 @@ export async function onRequest(context) { ```typescript interface Env { RATE_LIMIT: KVNamespace; } - async function rateLimitMiddleware(context: EventContext) { const clientIP = context.request.headers.get('CF-Connecting-IP') || 'unknown'; const key = `ratelimit:${clientIP}`; const count = parseInt(await context.env.RATE_LIMIT.get(key) || '0'); - if (count >= 100) return new Response('Rate limit exceeded', { status: 429 }); - await context.env.RATE_LIMIT.put(key, (count + 1).toString(), { expirationTtl: 3600 }); return context.next(); } @@ -78,17 +65,12 @@ async function rateLimitMiddleware(context: EventContext) { ```typescript export async function onRequestPost(context) { const contentType = context.request.headers.get('content-type') || ''; - if (contentType.includes('application/json')) { - const data = await context.request.json(); - return Response.json({ received: data }); + return Response.json({ received: await context.request.json() }); } - if (contentType.includes('application/x-www-form-urlencoded')) { - const formData = await context.request.formData(); - return Response.json({ received: Object.fromEntries(formData) }); + return Response.json({ received: Object.fromEntries(await context.request.formData()) }); } - if (contentType.includes('multipart/form-data')) { const formData = await context.request.formData(); const file = formData.get('file') as File; @@ -97,7 +79,6 @@ export async function onRequestPost(context) { return Response.json({ uploaded: file.name }); } } - return new Response('Unsupported content type', { status: 400 }); } ``` @@ -108,14 +89,12 @@ export async function onRequestPost(context) { export async function onRequest(context) { const cache = caches.default; const cacheKey = new Request(context.request.url, context.request); - let response = await cache.match(cacheKey); if (!response) { response = new Response('Hello World'); response.headers.set('Cache-Control', 'public, max-age=3600'); context.waitUntil(cache.put(cacheKey, response.clone())); } - return response; } ``` @@ -125,41 +104,24 @@ export async function onRequest(context) { ```typescript export async function onRequest(context) { const url = new URL(context.request.url); - - if (url.pathname === '/old-page') { - return Response.redirect(`${url.origin}/new-page`, 301); - } - - if (url.protocol === 'http:') { - url.protocol = 'https:'; - return Response.redirect(url.toString(), 301); - } - + if (url.pathname === '/old-page') return Response.redirect(`${url.origin}/new-page`, 301); + if (url.protocol === 'http:') { url.protocol = 'https:'; return Response.redirect(url.toString(), 301); } return context.next(); } ``` ## Advanced Mode (`_worker.js`) -Replace `/functions` with `_worker.js` for full routing control (complex Workers, framework-generated output: Next.js, SvelteKit). +Replace `/functions` with `_worker.js` for full routing control (complex Workers, framework output: Next.js, SvelteKit). ```typescript // Module Worker syntax required; /functions ignored when _worker.js present // Manually call env.ASSETS.fetch() for static files; passThroughOnException() unavailable - -interface Env { - ASSETS: Fetcher; - KV: KVNamespace; -} - +interface Env { ASSETS: Fetcher; KV: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); - - if (url.pathname.startsWith('/api/')) { - return new Response('API response'); - } - + if (url.pathname.startsWith('/api/')) return new Response('API response'); return env.ASSETS.fetch(request); } } satisfies ExportedHandler;