cloudflare-development by mindrally/skills
npx skills add https://github.com/mindrally/skills --skill cloudflare-development本技能提供了在 Cloudflare 边缘平台上开发应用程序的全面指南,包括 Workers、Pages、KV 存储、D1 数据库、R2 对象存储和 Durable Objects。
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>;
interface Env {
// KV 命名空间
MY_KV: KVNamespace;
// D1 数据库
MY_DB: D1Database;
// R2 存储桶
MY_BUCKET: R2Bucket;
// Durable Objects
MY_DURABLE_OBJECT: DurableObjectNamespace;
// 环境变量
API_KEY: string;
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
ctx.waitUntil() 处理后台任务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
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:' });
// 参数化查询(防止 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();
-- 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);
// 上传对象
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');
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);
});
}
}
my-pages-project/
├── public/ # 静态资源
├── functions/ # Pages Functions
│ ├── api/
│ │ └── [endpoint].ts
│ └── _middleware.ts
├── src/ # 应用程序源代码
└── wrangler.toml # 配置
// 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 });
};
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;
}
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);
}
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;
}
// 缓存 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;
}
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;
},
};
# 启动本地开发服务器
wrangler dev
# 使用本地持久化运行
wrangler dev --persist
# 使用特定环境测试
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('对有效请求返回 200', async () => {
const response = await worker.fetch('/api/health');
expect(response.status).toBe(200);
});
});
# 部署到生产环境
wrangler deploy
# 部署到特定环境
wrangler deploy --env production
# 部署密钥
wrangler secret put API_KEY
# .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 }}
每周安装次数
95
代码仓库
GitHub 星标数
43
首次出现时间
Jan 25, 2026
安全审计
安装于
gemini-cli80
opencode79
codex74
cursor72
claude-code72
github-copilot71
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.
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;
}
ctx.waitUntil() for background tasksname = "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"]
// 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:' });
// 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);
// 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');
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);
});
}
}
my-pages-project/
├── public/ # Static assets
├── functions/ # Pages Functions
│ ├── api/
│ │ └── [endpoint].ts
│ └── _middleware.ts
├── src/ # Application source
└── wrangler.toml # Configuration
// 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
# .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 }}
Weekly Installs
95
Repository
GitHub Stars
43
First Seen
Jan 25, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli80
opencode79
codex74
cursor72
claude-code72
github-copilot71