Best for
- Building serverless applications on the edge
- Implementing edge databases (D1 SQLite)
- Working with object storage (R2) or key-value stores (KV)
einverne/dotfiles/claude/skills/cloudflare/SKILL.md
Guide for building applications on Cloudflare's edge platform. Use when implementing serverless functions (Workers), edge databases (D1), storage (R2, KV), real-time apps (Durable Objects), AI features (Workers AI, AI Gateway), static sites (Pages), or any edge computing solutions.
Decision brief
Cloudflare Developer Platform is a comprehensive edge computing ecosystem for building full-stack applications on Cloudflare's global network. It includes serverless functions, databases, storage, AI/ML capabilities, and static site hosting.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/einverne/dotfiles --skill "claude/skills/cloudflare"Inspect the Agent Skill "cloudflare" from https://github.com/einverne/dotfiles/blob/7d18cf4fefdeec05853c7420cc0499dde1e88b27/claude/skills/cloudflare/SKILL.md at commit 7d18cf4fefdeec05853c7420cc0499dde1e88b27. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Getting Started with Workers” section in the pinned source before continuing.
bash npm create svelte@latest npm install -D @sveltejs/adapter-cloudflare bash
1. Local Development: Use wrangler dev for testing 2. Type Safety: Use TypeScript with @cloudflare/workers-types 3. Testing: Use Vitest with unstabledev() 4. CI/CD: GitHub Actions with cloudflare/wrangler-action 5. Gradual Deployments: Use percentage-based rollouts
[ ] Install Wrangler CLI (npm install -g wrangler)
[ ] Install Wrangler CLI (npm install -g wrangler)
Permission review
The documentation asks the agent to run terminal commands or scripts.
npm install -g wranglerThe documentation includes network, browsing, or remote request actions.
async fetch(request: Request, env: Env): Promise<Response> {The documentation includes network, browsing, or remote request actions.
async fetch(request: Request, env: Env) {The documentation asks the agent to create, modify, or delete local files.
await env.R2_BUCKET.delete("path/to/file.jpg");The documentation asks the agent to run terminal commands or scripts.
npx create-next-app@latest my-appEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 90/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 119 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Cloudflare Developer Platform is a comprehensive edge computing ecosystem for building full-stack applications on Cloudflare's global network. It includes serverless functions, databases, storage, AI/ML capabilities, and static site hosting.
Use this skill when:
Cloudflare's Edge Network: Code runs on servers globally distributed across 300+ cities, executing requests from the nearest location for ultra-low latency.
Key Components:
V8 Isolates: Lightweight execution environments (faster than containers) with:
Handler Types:
fetch: HTTP requestsscheduled: Cron jobsqueue: Message processingtail: Log aggregationemail: Email handlingalarm: Durable Object timers# Install Wrangler CLI
npm install -g wrangler
# Login to Cloudflare
wrangler login
# Create new project
wrangler init my-worker
cd my-worker
# Start local development
wrangler dev
# Deploy to production
wrangler deploy
// src/index.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return new Response('Hello from Cloudflare Workers!');
}
};
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
# Environment variables
[vars]
ENVIRONMENT = "production"
# Bindings (added per product below)
Use Cases: Relational data, complex queries, ACID transactions
Setup:
# Create database
wrangler d1 create my-database
# Add to wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "YOUR_DATABASE_ID"
# Generate and apply schema
wrangler d1 execute my-database --file=./schema.sql
Usage:
export default {
async fetch(request: Request, env: Env) {
// Query
const result = await env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(userId).first();
// Insert
await env.DB.prepare(
"INSERT INTO users (name, email) VALUES (?, ?)"
).bind("Alice", "[email protected]").run();
// Batch (atomic)
await env.DB.batch([
env.DB.prepare("UPDATE accounts SET balance = balance - 100 WHERE id = ?").bind(user1),
env.DB.prepare("UPDATE accounts SET balance = balance + 100 WHERE id = ?").bind(user2)
]);
return new Response(JSON.stringify(result));
}
};
Key Features:
Use Cases: Cache, sessions, feature flags, rate limiting
Setup:
# Create namespace
wrangler kv:namespace create MY_KV
# Add to wrangler.toml
[[kv_namespaces]]
binding = "KV"
id = "YOUR_NAMESPACE_ID"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Put with TTL
await env.KV.put("session:token", JSON.stringify(data), {
expirationTtl: 3600 // 1 hour
});
// Get
const data = await env.KV.get("session:token", "json");
// Delete
await env.KV.delete("session:token");
// List with prefix
const list = await env.KV.list({ prefix: "user:123:" });
return new Response(JSON.stringify(data));
}
};
Key Features:
Use Cases: File storage, media hosting, backups, static assets
Setup:
# Create bucket
wrangler r2 bucket create my-bucket
# Add to wrangler.toml
[[r2_buckets]]
binding = "R2_BUCKET"
bucket_name = "my-bucket"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Put object
await env.R2_BUCKET.put("path/to/file.jpg", fileBuffer, {
httpMetadata: {
contentType: "image/jpeg"
}
});
// Get object
const object = await env.R2_BUCKET.get("path/to/file.jpg");
if (!object) {
return new Response("Not found", { status: 404 });
}
// Stream response
return new Response(object.body, {
headers: {
"Content-Type": object.httpMetadata?.contentType || "application/octet-stream"
}
});
// Delete
await env.R2_BUCKET.delete("path/to/file.jpg");
// List
const list = await env.R2_BUCKET.list({ prefix: "uploads/" });
}
};
Key Features:
Use Cases: Real-time apps, WebSockets, coordination, stateful logic
Setup:
# wrangler.toml
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "my-worker"
Usage:
// Define Durable Object class
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
}
async fetch(request: Request) {
// Get current count
let count = (await this.state.storage.get<number>('count')) || 0;
// Increment
count++;
await this.state.storage.put('count', count);
return new Response(JSON.stringify({ count }));
}
}
// Use in Worker
export default {
async fetch(request: Request, env: Env) {
// Get Durable Object instance
const id = env.COUNTER.idFromName("global-counter");
const counter = env.COUNTER.get(id);
// Forward request
return counter.fetch(request);
}
};
WebSocket Example:
export class ChatRoom {
state: DurableObjectState;
sessions: Set<WebSocket>;
constructor(state: DurableObjectState) {
this.state = state;
this.sessions = new Set();
}
async fetch(request: Request) {
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
this.state.acceptWebSocket(server);
this.sessions.add(server);
return new Response(null, { status: 101, webSocket: client });
}
async webSocketMessage(ws: WebSocket, message: string) {
// Broadcast to all connected clients
for (const session of this.sessions) {
session.send(message);
}
}
async webSocketClose(ws: WebSocket) {
this.sessions.delete(ws);
}
}
Key Features:
Use Cases: Background jobs, email sending, async processing
Setup:
# wrangler.toml
[[queues.producers]]
binding = "MY_QUEUE"
queue = "my-queue"
[[queues.consumers]]
queue = "my-queue"
max_batch_size = 10
max_batch_timeout = 30
Usage:
// Producer: Send messages
export default {
async fetch(request: Request, env: Env) {
await env.MY_QUEUE.send({
type: 'email',
to: '[email protected]',
subject: 'Welcome!'
});
return new Response('Message queued');
}
};
// Consumer: Process messages
export default {
async queue(batch: MessageBatch<any>, env: Env) {
for (const message of batch.messages) {
try {
await processMessage(message.body);
message.ack(); // Acknowledge success
} catch (error) {
message.retry(); // Retry on failure
}
}
}
};
Key Features:
Use Cases: Run AI models directly on the edge
Setup:
# wrangler.toml
[ai]
binding = "AI"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Text generation
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [
{ role: 'user', content: 'What is edge computing?' }
]
});
// Image classification
const imageResponse = await env.AI.run('@cf/microsoft/resnet-50', {
image: imageBuffer
});
// Text embeddings
const embeddings = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: 'Hello world'
});
return new Response(JSON.stringify(response));
}
};
Available Models:
Use Cases: Unified interface for AI providers with caching, rate limiting, analytics
Setup:
// OpenAI via AI Gateway
const response = await fetch(
'https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai/chat/completions',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
})
}
);
Features:
Use Cases: Build AI agents with tools and workflows
import { Agent } from '@cloudflare/agents';
export default {
async fetch(request: Request, env: Env) {
const agent = new Agent({
model: '@cf/meta/llama-3-8b-instruct',
tools: [
{
name: 'get_weather',
description: 'Get current weather',
parameters: {
type: 'object',
properties: {
location: { type: 'string' }
}
},
handler: async ({ location }) => {
// Fetch weather data
return { temperature: 72, conditions: 'sunny' };
}
}
]
});
const result = await agent.run('What is the weather in San Francisco?');
return new Response(JSON.stringify(result));
}
};
Use Cases: Build retrieval-augmented generation applications
import { VectorizeIndex } from '@cloudflare/workers-types';
export default {
async fetch(request: Request, env: Env) {
// Generate embeddings
const embeddings = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: query
});
// Search vector database
const results = await env.VECTORIZE_INDEX.query(embeddings.data[0], {
topK: 5
});
// Generate response with context
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [
{
role: 'system',
content: `Context: ${results.matches.map(m => m.metadata.text).join('\n')}`
},
{ role: 'user', content: query }
]
});
return new Response(JSON.stringify(response));
}
};
Deployment:
# Deploy via Git (recommended)
# Connect GitHub repo in Cloudflare dashboard
# Or deploy via CLI
wrangler pages deploy ./dist
Directory-based routing in functions/:
functions/
├── api/
│ ├── users/
│ │ └── [id].ts # /api/users/:id
│ └── posts.ts # /api/posts
└── _middleware.ts # Global middleware
Example Function:
// functions/api/users/[id].ts
export async function onRequestGet(context) {
const { params, env } = context;
const user = await env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(params.id).first();
return new Response(JSON.stringify(user), {
headers: { 'Content-Type': 'application/json' }
});
}
Middleware:
// functions/_middleware.ts
export async function onRequest(context) {
const start = Date.now();
const response = await context.next();
const duration = Date.now() - start;
console.log(`${context.request.method} ${context.request.url} - ${duration}ms`);
return response;
}
Next.js:
npx create-next-app@latest my-app
cd my-app
npm install -D @cloudflare/next-on-pages
npx @cloudflare/next-on-pages
wrangler pages deploy .vercel/output/static
Remix:
npx create-remix@latest --template cloudflare/remix
Astro:
npm create astro@latest
# Select "Cloudflare" adapter during setup
SvelteKit:
npm create svelte@latest
npm install -D @sveltejs/adapter-cloudflare
# Development
wrangler dev # Local development server
wrangler dev --remote # Dev on real Cloudflare infrastructure
# Deployment
wrangler deploy # Deploy to production
wrangler deploy --dry-run # Preview changes without deploying
# Logs
wrangler tail # Real-time logs
wrangler tail --format pretty # Formatted logs
# Versions
wrangler deployments list # List deployments
wrangler rollback [version] # Rollback to previous version
# Secrets
wrangler secret put SECRET_NAME # Add secret
wrangler secret list # List secrets
wrangler secret delete SECRET_NAME # Delete secret
# Create projects
wrangler init my-worker # Create Worker
wrangler pages project create # Create Pages project
# Database
wrangler d1 create my-db # Create D1 database
wrangler d1 execute my-db --file=schema.sql
wrangler d1 execute my-db --command="SELECT * FROM users"
# KV
wrangler kv:namespace create MY_KV
wrangler kv:key put --binding=MY_KV "key" "value"
wrangler kv:key get --binding=MY_KV "key"
# R2
wrangler r2 bucket create my-bucket
wrangler r2 object put my-bucket/file.txt --file=./file.txt
┌─────────────────────────────────────────┐
│ Cloudflare Pages (Frontend) │
│ Next.js / Remix / Astro / SvelteKit │
└──────────────────┬──────────────────────┘
│
┌──────────────────▼──────────────────────┐
│ Workers (API Layer / BFF) │
│ - Routing │
│ - Authentication │
│ - Business logic │
└─┬──────┬──────┬──────┬──────┬───────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────────────┐
│ D1 │ │ KV │ │ R2 │ │ DO │ │ Workers AI │
└────┘ └────┘ └────┘ └────┘ └────────────┘
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// KV: Fast cache
const cached = await env.KV.get(url.pathname);
if (cached) return new Response(cached);
// D1: Structured data
const user = await env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(userId).first();
// R2: Media files
const avatar = await env.R2_BUCKET.get(`avatars/${user.id}.jpg`);
// Durable Objects: Real-time coordination
const chat = env.CHAT_ROOM.get(env.CHAT_ROOM.idFromName(roomId));
// Queue: Async processing
await env.EMAIL_QUEUE.send({ to: user.email, template: 'welcome' });
return new Response(JSON.stringify({ user, avatar }));
}
};
import { verifyJWT, createJWT } from './jwt';
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Login
if (url.pathname === '/api/login') {
const { email, password } = await request.json();
const user = await env.DB.prepare(
"SELECT * FROM users WHERE email = ?"
).bind(email).first();
if (!user || !await verifyPassword(password, user.password_hash)) {
return new Response('Invalid credentials', { status: 401 });
}
const token = await createJWT({ userId: user.id }, env.JWT_SECRET);
return new Response(JSON.stringify({ token }), {
headers: { 'Content-Type': 'application/json' }
});
}
// Protected route
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.replace('Bearer ', '');
const payload = await verifyJWT(token, env.JWT_SECRET);
// Store session in KV
await env.KV.put(`session:${payload.userId}`, JSON.stringify(payload), {
expirationTtl: 86400 // 24 hours
});
return new Response('Authenticated');
}
};
export default {
async fetch(request: Request, env: Env) {
const cache = caches.default;
const cacheKey = new Request(request.url);
// Check cache
let response = await cache.match(cacheKey);
if (response) return response;
// Check KV (distributed cache)
const kvCached = await env.KV.get(request.url);
if (kvCached) {
response = new Response(kvCached);
await cache.put(cacheKey, response.clone());
return response;
}
// Fetch from origin (D1, R2, etc.)
const data = await fetchFromOrigin(request, env);
response = new Response(data);
// Store in both caches
await cache.put(cacheKey, response.clone());
await env.KV.put(request.url, data, { expirationTtl: 3600 });
return response;
}
};
Response.body streams for large fileswrangler secret for API keyswrangler dev for testing@cloudflare/workers-typesunstable_dev()cloudflare/wrangler-actionimport { Hono } from 'hono';
const app = new Hono();
app.get('/api/users/:id', async (c) => {
const user = await c.env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(c.req.param('id')).first();
return c.json(user);
});
app.post('/api/users', async (c) => {
const { name, email } = await c.req.json();
await c.env.DB.prepare(
"INSERT INTO users (name, email) VALUES (?, ?)"
).bind(name, email).run();
return c.json({ success: true }, 201);
});
export default app;
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
const imageKey = url.pathname.replace('/images/', '');
// Get from R2
const object = await env.R2_BUCKET.get(imageKey);
if (!object) {
return new Response('Not found', { status: 404 });
}
// Transform with Cloudflare Images
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'image/jpeg',
'Cache-Control': 'public, max-age=86400',
'cf-image-resize': JSON.stringify({
width: 800,
height: 600,
fit: 'cover'
})
}
});
}
};
async function rateLimit(ip: string, env: Env): Promise<boolean> {
const key = `ratelimit:${ip}`;
const limit = 100; // requests per minute
const window = 60; // seconds
const current = await env.KV.get(key);
const count = current ? parseInt(current) : 0;
if (count >= limit) {
return false; // Rate limit exceeded
}
await env.KV.put(key, (count + 1).toString(), {
expirationTtl: window
});
return true;
}
export default {
async fetch(request: Request, env: Env) {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
if (!await rateLimit(ip, env)) {
return new Response('Rate limit exceeded', { status: 429 });
}
return new Response('OK');
}
};
# wrangler.toml
[triggers]
crons = ["0 0 * * *"] # Daily at midnight
export default {
async scheduled(event: ScheduledEvent, env: Env) {
// Cleanup old sessions
const sessions = await env.KV.list({ prefix: 'session:' });
for (const key of sessions.keys) {
const session = await env.KV.get(key.name, 'json');
if (session.expiresAt < Date.now()) {
await env.KV.delete(key.name);
}
}
}
};
"Module not found" errors
package.jsonnpm install before deployingDatabase connection errors (D1)
wrangler d1 listwrangler d1 execute DB --file=schema.sqlKV not found errors
wrangler kv:namespace create MY_KVCold start timeout
CORS errors
return new Response(data, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
Deployment fails
wrangler --versionwrangler whoami# Real-time logs
wrangler tail
# Local debugging with breakpoints
wrangler dev --local
# Remote debugging
wrangler dev --remote
# Check deployment status
wrangler deployments list
| Need | Choose |
|---|---|
| Sub-millisecond reads | KV |
| SQL queries | D1 |
| Large files (>25MB) | R2 |
| Real-time WebSockets | Durable Objects |
| Async background jobs | Queues |
| ACID transactions | D1 |
| Strong consistency | Durable Objects |
| Zero egress costs | R2 |
| AI inference | Workers AI |
| Static site hosting | Pages |
| Serverless functions | Workers |
| Multi-provider AI | AI Gateway |
@cloudflare/next-on-pages adapternext.config.js for edge runtimewrangler pages deployserver.ts for Workerscontext.cloudflare.env@astrojs/cloudflare adapterastro.config.mjsAstro.locals.runtime.env@sveltejs/adapter-cloudflaresvelte.config.jsevent.platform.envnpm install -g wrangler)wrangler login)wrangler init)wrangler dev)wrangler deploy)Alternatives
mission69b/t2000
Publishing, upgrading, and deploying Sui Move packages. Use this skill when the user needs to publish a package, upgrade a published package, deploy to multiple networks, serialize transactions for multisig signing, run a local Sui network (localnet), prepare for Mainnet launch, monitor production deployments, or debug dry run failures. Also use when the user asks about sui client publish, sui client upgrade, UpgradeCap, upgrade policies, Published.toml, --serialize-output, localnet, mainnet lau
teng-lin/notebooklm-py
Complete API for Google NotebookLM - full programmatic access including features not in the web UI. Create notebooks, add sources, generate all artifact types, download in multiple formats. Activates on explicit /notebooklm or intent like "create a podcast about X"
TencentCloudBase/CloudBase-AI-Toolkit
Build production-ready AI agent backends using the CloudBase Agent Python SDK — create agents with LangGraph/CrewAI/LlamaIndex, serve them via FastAPI with AG-UI protocol streaming + OpenAI-compatible endpoints, add tools (bash, filesystem, MCP, code execution), memory (in-memory, TDAI, MySQL, MongoDB), observability (OpenTelemetry/Langfuse), and middleware (auth, logging). Use this skill when the user wants to create an AI agent server, build a chatbot backend, set up human-in-the-loop workflow
ok-helloworld/vibe-pentest
Race condition and TOCTOU testing for web apps. Use when testing one-time operations, concurrent HTTP abuse, rate-limit bypass, Turbo Intruder gates, HTTP/2 single-packet attacks, and CWE-362-style synchronization gaps.