mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
204
.agents/skills/cloudflare-deploy/references/pages/patterns.md
Normal file
204
.agents/skills/cloudflare-deploy/references/pages/patterns.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# Patterns
|
||||
|
||||
## API Routes
|
||||
|
||||
```typescript
|
||||
// functions/api/todos/[id].ts
|
||||
export const onRequestGet: PagesFunction<Env> = async ({ env, params }) => {
|
||||
const todo = await env.DB.prepare('SELECT * FROM todos WHERE id = ?').bind(params.id).first();
|
||||
if (!todo) return new Response('Not found', { status: 404 });
|
||||
return Response.json(todo);
|
||||
};
|
||||
|
||||
export const onRequestPut: PagesFunction<Env> = async ({ env, params, request }) => {
|
||||
const body = await request.json();
|
||||
await env.DB.prepare('UPDATE todos SET title = ?, completed = ? WHERE id = ?')
|
||||
.bind(body.title, body.completed, params.id).run();
|
||||
return Response.json({ success: true });
|
||||
};
|
||||
// Also: onRequestDelete, onRequestPost
|
||||
```
|
||||
|
||||
## Auth Middleware
|
||||
|
||||
```typescript
|
||||
// functions/_middleware.ts
|
||||
const auth: PagesFunction<Env> = async (context) => {
|
||||
if (context.request.url.includes('/public/')) return context.next();
|
||||
const authHeader = context.request.headers.get('Authorization');
|
||||
if (!authHeader?.startsWith('Bearer ')) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await verifyJWT(authHeader.substring(7), context.env.JWT_SECRET);
|
||||
context.data.user = payload;
|
||||
return context.next();
|
||||
} catch (err) {
|
||||
return new Response('Invalid token', { status: 401 });
|
||||
}
|
||||
};
|
||||
export const onRequest = [auth];
|
||||
```
|
||||
|
||||
## CORS
|
||||
|
||||
```typescript
|
||||
// functions/api/_middleware.ts
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
|
||||
};
|
||||
|
||||
export const onRequest: PagesFunction = async (context) => {
|
||||
if (context.request.method === 'OPTIONS') {
|
||||
return new Response(null, {headers: corsHeaders});
|
||||
}
|
||||
const response = await context.next();
|
||||
Object.entries(corsHeaders).forEach(([k, v]) => response.headers.set(k, v));
|
||||
return response;
|
||||
};
|
||||
```
|
||||
|
||||
## Form Handling
|
||||
|
||||
```typescript
|
||||
// functions/api/contact.ts
|
||||
export const onRequestPost: PagesFunction<Env> = async ({ request, env }) => {
|
||||
const formData = await request.formData();
|
||||
await env.QUEUE.send({name: formData.get('name'), email: formData.get('email')});
|
||||
return new Response('<h1>Thanks!</h1>', { headers: { 'Content-Type': 'text/html' } });
|
||||
};
|
||||
```
|
||||
|
||||
## Background Tasks
|
||||
|
||||
```typescript
|
||||
export const onRequestPost: PagesFunction = async ({ request, waitUntil }) => {
|
||||
const data = await request.json();
|
||||
waitUntil(fetch('https://api.example.com/webhook', {
|
||||
method: 'POST', body: JSON.stringify(data)
|
||||
}));
|
||||
return Response.json({ queued: true });
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
// functions/_middleware.ts
|
||||
const errorHandler: PagesFunction = async (context) => {
|
||||
try {
|
||||
return await context.next();
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
if (context.request.url.includes('/api/')) {
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
return new Response(`<h1>Error</h1><p>${error.message}</p>`, {
|
||||
status: 500, headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
}
|
||||
};
|
||||
export const onRequest = [errorHandler];
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
```typescript
|
||||
// functions/api/data.ts
|
||||
export const onRequestGet: PagesFunction<Env> = async ({ env, request }) => {
|
||||
const cacheKey = `data:${new URL(request.url).pathname}`;
|
||||
const cached = await env.KV.get(cacheKey, 'json');
|
||||
if (cached) return Response.json(cached, { headers: { 'X-Cache': 'HIT' } });
|
||||
|
||||
const data = await env.DB.prepare('SELECT * FROM data').first();
|
||||
await env.KV.put(cacheKey, JSON.stringify(data), {expirationTtl: 3600});
|
||||
return Response.json(data, {headers: {'X-Cache': 'MISS'}});
|
||||
};
|
||||
```
|
||||
|
||||
## Smart Placement for Database Apps
|
||||
|
||||
Enable Smart Placement for apps with D1 or centralized data sources:
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc
|
||||
{
|
||||
"name": "global-app",
|
||||
"placement": {
|
||||
"mode": "smart"
|
||||
},
|
||||
"d1_databases": [{
|
||||
"binding": "DB",
|
||||
"database_id": "your-db-id"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// functions/api/data.ts
|
||||
export const onRequestGet: PagesFunction<Env> = async ({ env }) => {
|
||||
// Smart Placement optimizes execution location over time
|
||||
// Balances user location vs database location
|
||||
const data = await env.DB.prepare('SELECT * FROM products LIMIT 10').all();
|
||||
return Response.json(data);
|
||||
};
|
||||
```
|
||||
|
||||
**Best for**: Read-heavy apps with D1/Durable Objects in specific regions.
|
||||
**Not needed**: Apps without data locality constraints or with evenly distributed traffic.
|
||||
|
||||
## Framework Integration
|
||||
|
||||
**Supported** (2026): SvelteKit, Astro, Nuxt, Qwik, Solid Start
|
||||
|
||||
```bash
|
||||
npm create cloudflare@latest my-app -- --framework=svelte
|
||||
```
|
||||
|
||||
### SvelteKit
|
||||
```typescript
|
||||
// src/routes/+page.server.ts
|
||||
export const load = async ({ platform }) => {
|
||||
const todos = await platform.env.DB.prepare('SELECT * FROM todos').all();
|
||||
return { todos: todos.results };
|
||||
};
|
||||
```
|
||||
|
||||
### Astro
|
||||
```astro
|
||||
---
|
||||
const { DB } = Astro.locals.runtime.env;
|
||||
const todos = await DB.prepare('SELECT * FROM todos').all();
|
||||
---
|
||||
<ul>{todos.results.map(t => <li>{t.title}</li>)}</ul>
|
||||
```
|
||||
|
||||
### Nuxt
|
||||
```typescript
|
||||
// server/api/todos.get.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { DB } = event.context.cloudflare.env;
|
||||
return await DB.prepare('SELECT * FROM todos').all();
|
||||
});
|
||||
```
|
||||
|
||||
**⚠️ Framework Status** (2026):
|
||||
- ✅ **Supported**: SvelteKit, Astro, Nuxt, Qwik, Solid Start
|
||||
- ❌ **Deprecated**: Next.js (`@cloudflare/next-on-pages`), Remix (`@remix-run/cloudflare-pages`)
|
||||
|
||||
For deprecated frameworks, see [gotchas.md](./gotchas.md#framework-specific) for migration options.
|
||||
|
||||
[Framework Guides](https://developers.cloudflare.com/pages/framework-guides/)
|
||||
|
||||
## Monorepo
|
||||
|
||||
Dashboard → Settings → Build → Root directory. Set to subproject (e.g., `apps/web`).
|
||||
|
||||
## Best Practices
|
||||
|
||||
**Performance**: Exclude static via `_routes.json`; cache with KV; keep bundle < 1MB
|
||||
**Security**: Use secrets (not vars); validate inputs; rate limit with KV/DO
|
||||
**Workflow**: Preview per branch; local dev with `wrangler pages dev`; instant rollbacks in Dashboard
|
||||
Reference in New Issue
Block a user