Durable Objects
hodgef/apiker
Create and review Cloudflare Durable Objects. An agent skill from hodgef/apiker.
Guide for building applications on Cloudflare's edge platform.
$ npx skills add einverne/dotfiles --skill cloudflare -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install einverne/dotfiles cloudflare --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude/skills/cloudflare .claude/skills/cloudflare && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .claude/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflareType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add einverne/dotfiles --skill cloudflare -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install einverne/dotfiles cloudflare --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .agents/skills && cp -r skills-src/claude/skills/cloudflare .agents/skills/cloudflare && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .agents/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add einverne/dotfiles --skill cloudflare -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install einverne/dotfiles cloudflare --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/claude/skills/cloudflare .cursor/skills/cloudflare && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .cursor/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/einverne/dotfiles.git --path claude/skills/cloudflare--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add einverne/dotfiles --skill cloudflare -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install einverne/dotfiles cloudflare --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/claude/skills/cloudflare .gemini/skills/cloudflare && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .gemini/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install einverne/dotfiles cloudflareInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add einverne/dotfiles --skill cloudflare -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .github/skills && cp -r skills-src/claude/skills/cloudflare .github/skills/cloudflare && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .github/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add einverne/dotfiles --skill cloudflare -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install einverne/dotfiles cloudflare --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/einverne/dotfiles.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/claude/skills/cloudflare .opencode/skills/cloudflare && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "cloudflare" agent skill from https://github.com/einverne/dotfiles/tree/master/claude/skills/cloudflare into .opencode/skills/cloudflare/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cloudflare", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
cloudflareGuide for building applications on Cloudflare's edge platform.
Cloudflare is an agent skill from einverne/dotfiles. 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.
Its SKILL.md is about 6.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Static sites and blogs and Serverless. It works with Cloudflare, Cloudflare Durable Objects, Workers AI and SQLite. The repository describes itself as: my personal dotfiles managed by dotbot, zinit. The licence is MIT.
5 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit c6c0686. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
wranglernpmnpxFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
gateway.ai.cloudflare.comAlso links to:
developers.cloudflare.comdiscord.cloudflare.comgithub.comcloudflarestatus.comFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
JWT_SECRETOPENAI_API_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Cloudflare loads about 6.7k tokens when it runs. Until then it costs about 73 tokens; SKILL.md has 1,050 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from einverne/dotfiles at commit c6c0686, republished under its MIT licence (© einverne). 1,050 words, ~6,670 tokens.
.claude/skills/cloudflare/SKILL.md (or your agent's skills folder).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.sqlUsage:
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", "alice@example.com").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 = 30Usage:
// Producer: Send messages
export default {
async fetch(request: Request, env: Env) {
await env.MY_QUEUE.send({
type: 'email',
to: 'user@example.com',
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 ./distDirectory-based routing in functions/:
functions/
├── api/
│ ├── users/
│ │ └── [id].ts # /api/users/:id
│ └── posts.ts # /api/posts
└── _middleware.ts # Global middlewareExample 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/staticRemix:
npx create-remix@latest --template cloudflare/remixAstro:
npm create astro@latest
# Select "Cloudflare" adapter during setupSvelteKit:
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 midnightexport 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)© einverne, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in claude/skills/cloudflare of einverne/dotfiles.
Open the folder on GitHubat commit c6c0686
Cloudflare next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Cloudflare this skilleinverne/dotfiles | 121 | — | ~6.7k | Automated safety check: Pass | MIT | |
| Durable Objectshodgef/apiker | 127 | 4 repos | ~1.5k | Automated safety check: Pass | MIT | |
| Apikerhodgef/apiker | 127 | — | ~1.4k | Automated safety check: Pass | MIT | |
| Cloudflare WorkersEpicenterHQ/epicenter | 4.8k | — | ~576 | Automated safety check: Pass | Custom licence | |
| Cloudflareericrisco/rsc-harness | 167 | — | ~2.6k | Automated safety check: Pass | MIT | |
| Cloudflare D1secondsky/claude-skills | 227 | — | ~3.9k | Automated safety check: Pass | MIT |
hodgef/apiker
Create and review Cloudflare Durable Objects. An agent skill from hodgef/apiker.
hodgef/apiker
Develop, review, and extend the Apiker library — a framework for building serverless REST APIs on Cloudflare Workers + Durable Objects.
EpicenterHQ/epicenter
Cloudflare Workers patterns for Worker runtime APIs, Durable Objects, KV, R2, D1, Queues, WebSockets, streaming responses, bindings, wrangler configuration, and deployment limits.
ericrisco/rsc-harness
A skill your agent uses when working on Cloudflare's edge platform — wrangler.jsonc bindings, choosing between D1/KV/R2/Durable Objects/Queues, deploying a Worker or SPA via Static Assets, or…
secondsky/claude-skills
Cloudflare D1 serverless SQLite on edge. An agent skill from secondsky/claude-skills.
curiositech/some_claude_skills
Cloudflare Workers, KV, Durable Objects, and edge computing development.
einverne/dotfiles
Browser automation, debugging, and performance analysis using Puppeteer CLI scripts.
einverne/dotfiles
Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction.
einverne/dotfiles
Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms.
einverne/dotfiles
Guide for implementing Google Gemini API audio capabilities - analyze audio with transcription, summarization, and understanding (up to 9.5 hours), plus generate speech with controllable TTS.
einverne/dotfiles
Guide for implementing Google Gemini API image generation - create high-quality images from text prompts using gemini-2.5-flash-image model.
einverne/dotfiles
Guide for implementing Google Gemini API image understanding - analyze images with captioning, classification, visual QA, object detection, segmentation, and multi-image comparison.
Categories
Guide for building applications on Cloudflare's edge platform. Cloudflare is an agent skill from einverne/dotfiles. Guide for building applications on Cloudflare's edge platform.
Cloudflare fits situations like: implementing serverless functions (Workers); edge databases (D1); real-time apps (Durable Objects); AI features (Workers AI.
Run `npx skills add einverne/dotfiles --skill cloudflare -a claude-code`. Or copy the skill folder (claude/skills/cloudflare in einverne/dotfiles) into .claude/skills/cloudflare in your project. Claude Code loads it when a task matches its description.
Run `npx skills add einverne/dotfiles --skill cloudflare -a codex`. Or copy the skill folder (claude/skills/cloudflare in einverne/dotfiles) into .agents/skills/cloudflare in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add einverne/dotfiles --skill cloudflare -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cloudflare, .gemini/skills/cloudflare, .github/skills/cloudflare and .opencode/skills/cloudflare in your project.
Going by SKILL.md and its folder, Cloudflare needs the command-line tools its instructions call (wrangler, npm and npx) and credentials named JWT_SECRET and OPENAI_API_KEY. Our summary lists: Python 3; Node.js; A credential in OPENAI_API_KEY.
SKILL.md names 5 domains. In commands or code: gateway.ai.cloudflare.com; the agent is likely to contact it when it follows the instructions. As links in the text: developers.cloudflare.com, discord.cloudflare.com, github.com and cloudflarestatus.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Cloudflare is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.7k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Cloudflare: Durable Objects (hodgef/apiker, 127 stars), Apiker (hodgef/apiker, 127 stars), Cloudflare Workers (EpicenterHQ/epicenter, 4.8k stars) and Cloudflare (ericrisco/rsc-harness, 167 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
einverne (a GitHub user) maintains it in einverne/dotfiles, which has 121 GitHub stars. The repository holds 39 skills in this directory. The repository was last updated on September 9, 2026.
Source: einverne/dotfiles on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.