Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/pages/patterns.md
1# Patterns23## API Routes45```typescript6// functions/api/todos/[id].ts7export const onRequestGet: PagesFunction<Env> = async ({ env, params }) => {8const todo = await env.DB.prepare('SELECT * FROM todos WHERE id = ?').bind(params.id).first();9if (!todo) return new Response('Not found', { status: 404 });10return Response.json(todo);11};1213export const onRequestPut: PagesFunction<Env> = async ({ env, params, request }) => {14const body = await request.json();15await env.DB.prepare('UPDATE todos SET title = ?, completed = ? WHERE id = ?')16.bind(body.title, body.completed, params.id).run();17return Response.json({ success: true });18};19// Also: onRequestDelete, onRequestPost20```2122## Auth Middleware2324```typescript25// functions/_middleware.ts26const auth: PagesFunction<Env> = async (context) => {27if (context.request.url.includes('/public/')) return context.next();28const authHeader = context.request.headers.get('Authorization');29if (!authHeader?.startsWith('Bearer ')) {30return new Response('Unauthorized', { status: 401 });31}3233try {34const payload = await verifyJWT(authHeader.substring(7), context.env.JWT_SECRET);35context.data.user = payload;36return context.next();37} catch (err) {38return new Response('Invalid token', { status: 401 });39}40};41export const onRequest = [auth];42```4344## CORS4546```typescript47// functions/api/_middleware.ts48const corsHeaders = {49'Access-Control-Allow-Origin': '*',50'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',51'Access-Control-Allow-Headers': 'Content-Type, Authorization'52};5354export const onRequest: PagesFunction = async (context) => {55if (context.request.method === 'OPTIONS') {56return new Response(null, {headers: corsHeaders});57}58const response = await context.next();59Object.entries(corsHeaders).forEach(([k, v]) => response.headers.set(k, v));60return response;61};62```6364## Form Handling6566```typescript67// functions/api/contact.ts68export const onRequestPost: PagesFunction<Env> = async ({ request, env }) => {69const formData = await request.formData();70await env.QUEUE.send({name: formData.get('name'), email: formData.get('email')});71return new Response('<h1>Thanks!</h1>', { headers: { 'Content-Type': 'text/html' } });72};73```7475## Background Tasks7677```typescript78export const onRequestPost: PagesFunction = async ({ request, waitUntil }) => {79const data = await request.json();80waitUntil(fetch('https://api.example.com/webhook', {81method: 'POST', body: JSON.stringify(data)82}));83return Response.json({ queued: true });84};85```8687## Error Handling8889```typescript90// functions/_middleware.ts91const errorHandler: PagesFunction = async (context) => {92try {93return await context.next();94} catch (error) {95console.error('Error:', error);96if (context.request.url.includes('/api/')) {97return Response.json({ error: error.message }, { status: 500 });98}99return new Response(`<h1>Error</h1><p>${error.message}</p>`, {100status: 500, headers: { 'Content-Type': 'text/html' }101});102}103};104export const onRequest = [errorHandler];105```106107## Caching108109```typescript110// functions/api/data.ts111export const onRequestGet: PagesFunction<Env> = async ({ env, request }) => {112const cacheKey = `data:${new URL(request.url).pathname}`;113const cached = await env.KV.get(cacheKey, 'json');114if (cached) return Response.json(cached, { headers: { 'X-Cache': 'HIT' } });115116const data = await env.DB.prepare('SELECT * FROM data').first();117await env.KV.put(cacheKey, JSON.stringify(data), {expirationTtl: 3600});118return Response.json(data, {headers: {'X-Cache': 'MISS'}});119};120```121122## Smart Placement for Database Apps123124Enable Smart Placement for apps with D1 or centralized data sources:125126```jsonc127// wrangler.jsonc128{129"name": "global-app",130"placement": {131"mode": "smart"132},133"d1_databases": [{134"binding": "DB",135"database_id": "your-db-id"136}]137}138```139140```typescript141// functions/api/data.ts142export const onRequestGet: PagesFunction<Env> = async ({ env }) => {143// Smart Placement optimizes execution location over time144// Balances user location vs database location145const data = await env.DB.prepare('SELECT * FROM products LIMIT 10').all();146return Response.json(data);147};148```149150**Best for**: Read-heavy apps with D1/Durable Objects in specific regions.151**Not needed**: Apps without data locality constraints or with evenly distributed traffic.152153## Framework Integration154155**Supported** (2026): SvelteKit, Astro, Nuxt, Qwik, Solid Start156157```bash158npm create cloudflare@latest my-app -- --framework=svelte159```160161### SvelteKit162```typescript163// src/routes/+page.server.ts164export const load = async ({ platform }) => {165const todos = await platform.env.DB.prepare('SELECT * FROM todos').all();166return { todos: todos.results };167};168```169170### Astro171```astro172---173const { DB } = Astro.locals.runtime.env;174const todos = await DB.prepare('SELECT * FROM todos').all();175---176<ul>{todos.results.map(t => <li>{t.title}</li>)}</ul>177```178179### Nuxt180```typescript181// server/api/todos.get.ts182export default defineEventHandler(async (event) => {183const { DB } = event.context.cloudflare.env;184return await DB.prepare('SELECT * FROM todos').all();185});186```187188**⚠️ Framework Status** (2026):189- ✅ **Supported**: SvelteKit, Astro, Nuxt, Qwik, Solid Start190- ❌ **Deprecated**: Next.js (`@cloudflare/next-on-pages`), Remix (`@remix-run/cloudflare-pages`)191192For deprecated frameworks, see [gotchas.md](./gotchas.md#framework-specific) for migration options.193194[Framework Guides](https://developers.cloudflare.com/pages/framework-guides/)195196## Monorepo197198Dashboard → Settings → Build → Root directory. Set to subproject (e.g., `apps/web`).199200## Best Practices201202**Performance**: Exclude static via `_routes.json`; cache with KV; keep bundle < 1MB203**Security**: Use secrets (not vars); validate inputs; rate limit with KV/DO204**Workflow**: Preview per branch; local dev with `wrangler pages dev`; instant rollbacks in Dashboard205