关于 cloudflare-development
面向使用 Cloudflare 边缘平台开发应用的技能,适用于使用 Workers、Pages 及相关存储的开发者,涵盖实现、安全、性能、测试和部署实践。
主要功能
- 01Workers 与 Pages 开发
- 02KV、D1 和 R2 使用指引
- 03Durable Objects 模式
- 04边缘安全实践
- 05测试与部署指引
适用场景
- 01实现 Cloudflare Worker API
- 02为边缘应用选择存储方案
- 03测试并部署 Cloudflare 应用
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill cloudflare-development安装后在 Claude Code 中输入/cloudflare-development确认已加载。
详细介绍
Cloudflare 开发最佳实践
概述
本技能提供了在 Cloudflare 边缘平台上开发应用的全面指南,涵盖 Workers、Pages、KV 存储、D1 数据库、R2 对象存储和 Durable Objects。
核心原则
- 编写轻量、快速的代码,针对边缘执行进行优化
- 尽量缩短冷启动时间和执行时长
- 针对不同用例选择合适的存储解决方案
- 遵循边缘计算安全最佳实践
- 利用 Cloudflare 的全球网络提升性能
Cloudflare Workers 指南
Worker 基本结构
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
try {
const url = new URL(request.url);
// Route handling
if (url.pathname === '/api/data') {
return handleApiRequest(request, env);
}
return new Response('Not Found', { status: 404 });
} catch (error) {
console.error('Worker error:', error);
return new Response('Internal Server Error', { status: 500 });
}
},
} satisfies ExportedHandler<Env>;
环境类型
interface Env {
// KV Namespaces
MY_KV: KVNamespace;
// D1 Databases
MY_DB: D1Database;
// R2 Buckets
MY_BUCKET: R2Bucket;
// Durable Objects
MY_DURABLE_OBJECT: DurableObjectNamespace;
// Environment Variables
API_KEY: string;
}
最佳实践
- 使用 TypeScript 确保类型安全
- 在边缘环境中妥善处理错误
- 实施恰当的请求验证
- 使用
ctx.waitUntil()处理后台任务 - 尽可能减少外部 API 调用
Wrangler 配置
wrangler.toml 结构
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"
[[d1_databases]]
binding = "MY_DB"
database_name = "my-database"
database_id = "def456"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
[durable_objects]
bindings = [
{ name = "MY_DURABLE_OBJECT", class_name = "MyDurableObject" }
]
[[migrations]]
tag = "v1"
new_classes = ["MyDurableObject"]
KV 存储指南
使用模式
// Writing to KV
await env.MY_KV.put('key', JSON.stringify(data), {
expirationTtl: 3600, // 1 hour
metadata: { version: '1.0' },
});
// Reading from KV
const value = await env.MY_KV.get('key', { type: 'json' });
// Listing keys
const list = await env.MY_KV.list({ prefix: 'user:' });
最佳实践
- 使用 KV 处理读密集型负载,并接受最终一致性
- 为缓存数据设置恰当的 TTL
- 使用元数据补充键的信息
- 实施缓存失效策略
- 注意 KV 的最终一致性模型
D1 数据库指南
查询模式
// Parameterized queries (prevent SQL injection)
const results = await env.MY_DB
.prepare('SELECT * FROM users WHERE id = ?')
.bind(userId)
.all();
// Batch operations
const batch = await env.MY_DB.batch([
env.MY_DB.prepare('INSERT INTO logs (message) VALUES (?)').bind('log1'),
env.MY_DB.prepare('INSERT INTO logs (message) VALUES (?)').bind('log2'),
]);
// First result only
const user = await env.MY_DB
.prepare('SELECT * FROM users WHERE email = ?')
.bind(email)
.first();
架构管理
-- migrations/0001_initial.sql
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
最佳实践
- 始终使用参数化查询
- 创建适当的索引
- 多次写入时使用批处理操作
- 保持查询简单高效
- 使用迁移管理架构变更
R2 对象存储指南
使用模式
// Upload object
await env.MY_BUCKET.put('uploads/file.pdf', fileData, {
httpMetadata: {
contentType: 'application/pdf',
},
customMetadata: {
uploadedBy: userId,
},
});
// Download object
const object = await env.MY_BUCKET.get('uploads/file.pdf');
if (object) {
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
},
});
}
// List objects
const list = await env.MY_BUCKET.list({ prefix: 'uploads/' });
// Delete object
await env.MY_BUCKET.delete('uploads/file.pdf');
最佳实践
- 设置恰当的内容类型
- 大文件使用分段上传
- 实施恰当的访问控制
- 使用预签名 URL 直接从客户端上传
- 使用有意义的前缀组织对象
Durable Objects 指南
实现
export class ChatRoom implements DurableObject {
private state: DurableObjectState;
private sessions: Map<WebSocket, { id: string }>;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
this.sessions = new Map();
}
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === '/websocket') {
const pair = new WebSocketPair();
await this.handleSession(pair[1]);
return new Response(null, { status: 101, webSocket: pair[0] });
}
return new Response('Not Found', { status: 404 });
}
async handleSession(webSocket: WebSocket) {
webSocket.accept();
webSocket.addEventListener('message', async (event) => {
// Handle messages
});
webSocket.addEventListener('close', () => {
this.sessions.delete(webSocket);
});
}
}
最佳实践
- 用于协调和有状态逻辑
- 妥善处理 WebSocket
- 使用存储 API 实现持久化
- 通过处理休眠状态来优化成本
- 针对单点协调模式进行设计
Cloudflare Pages 指南
项目结构
my-pages-project/
├── public/ # Static assets
├── functions/ # Pages Functions
│ ├── api/
│ │ └── [endpoint].ts
│ └── _middleware.ts
├── src/ # Application source
└── wrangler.toml # Configuration
Pages Functions
// functions/api/users.ts
export const onRequestGet: PagesFunction<Env> = async (context) => {
const users = await context.env.MY_DB
.prepare('SELECT * FROM users')
.all();
return Response.json(users.results);
};
export const onRequestPost: PagesFunction<Env> = async (context) => {
const body = await context.request.json();
// Handle POST
return Response.json({ success: true });
};
边缘安全最佳实践
请求验证
function validateRequest(request: Request): boolean {
// Check content type
const contentType = request.headers.get('Content-Type');
if (request.method === 'POST' && !contentType?.includes('application/json')) {
return false;
}
// Check origin (CORS)
const origin = request.headers.get('Origin');
if (origin && !ALLOWED_ORIGINS.includes(origin)) {
return false;
}
return true;
}
身份验证
async function verifyAuth(request: Request, env: Env): Promise<boolean> {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return false;
}
const token = authHeader.slice(7);
// Verify JWT or API key
return await verifyToken(token, env);
}
速率限制
async function checkRateLimit(ip: string, env: Env): Promise<boolean> {
const key = `ratelimit:${ip}`;
const current = await env.MY_KV.get(key, { type: 'json' }) as number || 0;
if (current >= 100) { // 100 requests per window
return false;
}
await env.MY_KV.put(key, JSON.stringify(current + 1), {
expirationTtl: 60, // 1 minute window
});
return true;
}
性能优化
缓存策略
// Cache API usage
const cache = caches.default;
async function handleRequest(request: Request): Promise<Response> {
// Check cache
const cached = await cache.match(request);
if (cached) {
return cached;
}
// Generate response
const response = await generateResponse(request);
// Cache response
const cacheResponse = new Response(response.body, response);
cacheResponse.headers.set('Cache-Control', 'public, max-age=3600');
ctx.waitUntil(cache.put(request, cacheResponse.clone()));
return cacheResponse;
}
后台处理
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Respond immediately
const response = Response.json({ status: 'accepted' });
// Process in background
ctx.waitUntil(processInBackground(request, env));
return response;
},
};
测试
本地开发
# Start local development server
wrangler dev
# Run with local persistence
wrangler dev --persist
# Test with specific environment
wrangler dev --env staging
单元测试
import { unstable_dev } from 'wrangler';
describe('Worker', () => {
let worker: UnstableDevWorker;
beforeAll(async () => {
worker = await unstable_dev('src/index.ts', {
experimental: { disableExperimentalWarning: true },
});
});
afterAll(async () => {
await worker.stop();
});
test('returns 200 for valid request', async () => {
const response = await worker.fetch('/api/health');
expect(response.status).toBe(200);
});
});
部署
生产环境部署
# Deploy to production
wrangler deploy
# Deploy to specific environment
wrangler deploy --env production
# Deploy with secrets
wrangler secret put API_KEY
CI/CD 集成
# .github/workflows/deploy.yml
name: Deploy Worker
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
应避免的常见问题
- 未能在边缘环境中妥善处理错误
- 进行过多外部 API 调用
- 忽视 Worker 的 CPU 和内存限制
- 未针对用例使用合适的存储
- 忘记 KV 的最终一致性
- 未实施恰当的速率限制
- 在代码中硬编码密钥
- 忽视冷启动优化
评论
支持 Markdown