cloudflare-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Development Best Practices

Cloudflare开发最佳实践

Overview

概述

This skill provides comprehensive guidelines for developing applications on Cloudflare's edge platform, including Workers, Pages, KV storage, D1 databases, R2 object storage, and Durable Objects.
本指南提供了在Cloudflare边缘平台上开发应用的全面准则,涵盖Workers、Pages、KV存储、D1数据库、R2对象存储及Durable Objects。

Core Principles

核心原则

  • Write lightweight, fast code optimized for edge execution
  • Minimize cold start times and execution duration
  • Use appropriate storage solutions for each use case
  • Follow security best practices for edge computing
  • Leverage Cloudflare's global network for performance
  • 编写轻量、快速的代码,针对边缘执行进行优化
  • 最小化冷启动时间和执行时长
  • 为不同使用场景选择合适的存储方案
  • 遵循边缘计算的安全最佳实践
  • 利用Cloudflare全球网络提升性能

Cloudflare Workers Guidelines

Cloudflare Workers指南

Basic Worker Structure

基础Worker结构

typescript
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>;
typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      const url = new URL(request.url);

      // 路由处理
      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>;

Environment Types

环境类型

typescript
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
interface Env {
  // KV命名空间
  MY_KV: KVNamespace;

  // D1数据库
  MY_DB: D1Database;

  // R2存储桶
  MY_BUCKET: R2Bucket;

  // Durable Objects
  MY_DURABLE_OBJECT: DurableObjectNamespace;

  // 环境变量
  API_KEY: string;
}

Best Practices

最佳实践

  • Use TypeScript for type safety
  • Handle errors at the edge appropriately
  • Implement proper request validation
  • Use
    ctx.waitUntil()
    for background tasks
  • Minimize external API calls when possible
  • 使用TypeScript保证类型安全
  • 在边缘端妥善处理错误
  • 实现恰当的请求验证
  • 使用
    ctx.waitUntil()
    处理后台任务
  • 尽可能减少外部API调用

Wrangler Configuration

Wrangler配置

wrangler.toml Structure

wrangler.toml结构

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"]
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 Storage Guidelines

KV存储指南

Usage Patterns

使用模式

typescript
// 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:' });
typescript
// 写入KV
await env.MY_KV.put('key', JSON.stringify(data), {
  expirationTtl: 3600, // 1小时
  metadata: { version: '1.0' },
});

// 读取KV
const value = await env.MY_KV.get('key', { type: 'json' });

// 列出键
const list = await env.MY_KV.list({ prefix: 'user:' });

Best Practices

最佳实践

  • Use KV for read-heavy workloads with eventual consistency
  • Set appropriate TTLs for cached data
  • Use metadata for additional key information
  • Implement cache invalidation strategies
  • Be aware of KV's eventual consistency model
  • 将KV用于读密集型、最终一致性的工作负载
  • 为缓存数据设置合适的TTL
  • 使用元数据存储额外的键信息
  • 实现缓存失效策略
  • 注意KV的最终一致性模型

D1 Database Guidelines

D1数据库指南

Query Patterns

查询模式

typescript
// 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();
typescript
// 参数化查询(防止SQL注入)
const results = await env.MY_DB
  .prepare('SELECT * FROM users WHERE id = ?')
  .bind(userId)
  .all();

// 批量操作
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'),
]);

// 仅获取第一条结果
const user = await env.MY_DB
  .prepare('SELECT * FROM users WHERE email = ?')
  .bind(email)
  .first();

Schema Management

架构管理

sql
-- 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);
sql
-- 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);

Best Practices

最佳实践

  • Always use parameterized queries
  • Create appropriate indexes
  • Use batch operations for multiple writes
  • Keep queries simple and efficient
  • Use migrations for schema changes
  • 始终使用参数化查询
  • 创建合适的索引
  • 对多次写入操作使用批量处理
  • 保持查询简洁高效
  • 使用迁移来管理架构变更

R2 Object Storage Guidelines

R2对象存储指南

Usage Patterns

使用模式

typescript
// 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');
typescript
// 上传对象
await env.MY_BUCKET.put('uploads/file.pdf', fileData, {
  httpMetadata: {
    contentType: 'application/pdf',
  },
  customMetadata: {
    uploadedBy: userId,
  },
});

// 下载对象
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',
    },
  });
}

// 列出对象
const list = await env.MY_BUCKET.list({ prefix: 'uploads/' });

// 删除对象
await env.MY_BUCKET.delete('uploads/file.pdf');

Best Practices

最佳实践

  • Set appropriate content types
  • Use multipart uploads for large files
  • Implement proper access controls
  • Use presigned URLs for direct client uploads
  • Organize objects with logical prefixes
  • 设置合适的内容类型
  • 对大文件使用分段上传
  • 实现恰当的访问控制
  • 使用预签名URL支持客户端直接上传
  • 用逻辑前缀组织对象

Durable Objects Guidelines

Durable Objects指南

Implementation

实现示例

typescript
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);
    });
  }
}
typescript
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) => {
      // 处理消息
    });

    webSocket.addEventListener('close', () => {
      this.sessions.delete(webSocket);
    });
  }
}

Best Practices

最佳实践

  • Use for coordination and stateful logic
  • Implement proper WebSocket handling
  • Use storage API for persistence
  • Handle hibernation for cost optimization
  • Design for single-point-of-coordination patterns
  • 用于协调和有状态逻辑场景
  • 实现恰当的WebSocket处理
  • 使用存储API实现持久化
  • 利用休眠机制优化成本
  • 设计单点协调模式

Cloudflare Pages Guidelines

Cloudflare Pages指南

Project Structure

项目结构

my-pages-project/
├── public/           # Static assets
├── functions/        # Pages Functions
│   ├── api/
│   │   └── [endpoint].ts
│   └── _middleware.ts
├── src/              # Application source
└── wrangler.toml     # Configuration
my-pages-project/
├── public/           # 静态资源
├── functions/        # Pages Functions
│   ├── api/
│   │   └── [endpoint].ts
│   └── _middleware.ts
├── src/              # 应用源码
└── wrangler.toml     # 配置文件

Pages Functions

Pages Functions示例

typescript
// 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 });
};
typescript
// 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();
  // 处理POST请求
  return Response.json({ success: true });
};

Edge Security Best Practices

边缘安全最佳实践

Request Validation

请求验证

typescript
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;
}
typescript
function validateRequest(request: Request): boolean {
  // 检查内容类型
  const contentType = request.headers.get('Content-Type');
  if (request.method === 'POST' && !contentType?.includes('application/json')) {
    return false;
  }

  // 检查来源(CORS)
  const origin = request.headers.get('Origin');
  if (origin && !ALLOWED_ORIGINS.includes(origin)) {
    return false;
  }

  return true;
}

Authentication

身份验证

typescript
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);
}
typescript
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);
  // 验证JWT或API密钥
  return await verifyToken(token, env);
}

Rate Limiting

请求限流

typescript
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;
}
typescript
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次请求
    return false;
  }

  await env.MY_KV.put(key, JSON.stringify(current + 1), {
    expirationTtl: 60, // 1分钟窗口
  });

  return true;
}

Performance Optimization

性能优化

Caching Strategies

缓存策略

typescript
// 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;
}
typescript
// 使用Cache API
const cache = caches.default;

async function handleRequest(request: Request): Promise<Response> {
  // 检查缓存
  const cached = await cache.match(request);
  if (cached) {
    return cached;
  }

  // 生成响应
  const response = await generateResponse(request);

  // 缓存响应
  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;
}

Background Processing

后台处理

typescript
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;
  },
};
typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // 立即返回响应
    const response = Response.json({ status: 'accepted' });

    // 在后台处理任务
    ctx.waitUntil(processInBackground(request, env));

    return response;
  },
};

Testing

测试

Local Development

本地开发

bash
undefined
bash
undefined

Start local development server

启动本地开发服务器

wrangler dev
wrangler dev

Run with local persistence

启用本地持久化

wrangler dev --persist
wrangler dev --persist

Test with specific environment

使用指定环境测试

wrangler dev --env staging
undefined
wrangler dev --env staging
undefined

Unit Testing

单元测试

typescript
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);
  });
});
typescript
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('有效请求返回200状态码', async () => {
    const response = await worker.fetch('/api/health');
    expect(response.status).toBe(200);
  });
});

Deployment

部署

Production Deployment

生产环境部署

bash
undefined
bash
undefined

Deploy to production

部署到生产环境

wrangler deploy
wrangler deploy

Deploy to specific environment

部署到指定环境

wrangler deploy --env production
wrangler deploy --env production

Deploy with secrets

部署时配置密钥

wrangler secret put API_KEY
undefined
wrangler secret put API_KEY
undefined

CI/CD Integration

CI/CD集成

yaml
undefined
yaml
undefined

.github/workflows/deploy.yml

.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 }}
undefined
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 }}
undefined

Common Pitfalls to Avoid

需避免的常见陷阱

  1. Not handling errors at the edge properly
  2. Making too many external API calls
  3. Ignoring Worker CPU and memory limits
  4. Not using appropriate storage for use case
  5. Forgetting eventual consistency in KV
  6. Not implementing proper rate limiting
  7. Hardcoding secrets in code
  8. Ignoring cold start optimization
  1. 未在边缘端妥善处理错误
  2. 发起过多外部API调用
  3. 忽略Worker的CPU和内存限制
  4. 未为使用场景选择合适的存储方案
  5. 忘记KV的最终一致性特性
  6. 未实现恰当的请求限流
  7. 在代码中硬编码密钥
  8. 忽略冷启动优化