
Cloudflare Workers
- 76 installs
- 44 repo stars
- Updated May 22, 2026
- bagelhole/devops-security-agent-skills
cloudflare-workers is a Claude Code skill that builds and deploys edge functions (APIs, cron jobs, middleware) with Cloudflare Workers and Wrangler.
About
This skill builds and deploys edge functions with Cloudflare Workers. It covers Wrangler project setup and commands, wrangler.toml config, API routers, middleware like rate limiting, cron jobs, and KV, D1, and R2 storage bindings. A developer uses it to build lightweight edge APIs, middleware, and scheduled jobs without managing servers. It matters for running low-latency serverless logic at the edge.
- Builds and deploys edge functions with Cloudflare Workers and Wrangler
- Covers APIs, middleware, cron jobs, and KV/D1/R2 storage bindings
- Includes Wrangler config, secrets, rate limiting, and tail logging
Cloudflare Workers by the numbers
- 76 all-time installs (skills.sh)
- Ranked #633 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
cloudflare-workers capabilities & compatibility
Free plan supports 100,000 requests/day; needs wrangler login or CLOUDFLARE_API_TOKEN
- Capabilities
- cloudflare pages · cloudflare r2 · cdn setup
- Works with
- cloudflare
- Use cases
- api development · devops
- Platforms
- macOS · Linux · Windows
- Pricing
- Freemium
What cloudflare-workers says it does
Deploy JavaScript and TypeScript functions to Cloudflare's global edge network with sub-millisecond cold starts.
Cloudflare account (free plan supports 100,000 requests/day).
npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill cloudflare-workersAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 76 |
|---|---|
| repo stars | ★ 44 |
| Last updated | May 22, 2026 |
| Repository | bagelhole/devops-security-agent-skills ↗ |
What it does
Build and deploy edge functions (APIs, middleware, cron jobs) with Cloudflare Workers and Wrangler.
Who is it for?
Building lightweight edge APIs, middleware, and cron jobs on Cloudflare Workers with storage bindings
Skip if: Heavy, stateful, long-running backend workloads that exceed edge runtime limits
When should I use this skill?
Building edge APIs or microservices, adding middleware, running cron jobs, or serving from KV/D1/R2
What you get
Serverless edge functions deployed globally with storage bindings and secrets managed via Wrangler.
- Deployed Worker
- wrangler.toml config
- Edge API router or middleware
By the numbers
- Free plan supports 100,000 requests/day
- Covers 3 storage bindings: KV, D1, R2
Files
Cloudflare Workers
Deploy JavaScript and TypeScript functions to Cloudflare's global edge network with sub-millisecond cold starts.
When to Use
- Building lightweight APIs and microservices at the edge.
- Adding middleware (auth, rate limiting, header injection) in front of origin servers.
- Running cron jobs on a schedule without maintaining infrastructure.
- Processing webhooks, image transformations, or A/B testing logic.
- Serving dynamic content from KV, D1, or R2 storage bindings.
Prerequisites
- Node.js 18+ installed locally.
- Wrangler CLI:
npm install -g wrangler. - Cloudflare account (free plan supports 100,000 requests/day).
- Authenticated:
wrangler loginor setCLOUDFLARE_API_TOKEN.
Quick Start
# Scaffold a new Worker project
npm create cloudflare@latest my-worker
cd my-worker
# Login to Cloudflare
npx wrangler login
# Start local development server (port 8787)
npx wrangler dev
# Deploy to production
npx wrangler deployEssential Wrangler Commands
# Local development with remote bindings (KV, D1, R2)
npx wrangler dev --remote
# Deploy to a specific environment
npx wrangler deploy --env staging
# Set a secret (prompts for value)
npx wrangler secret put API_TOKEN
npx wrangler secret put API_TOKEN --env staging
# List secrets
npx wrangler secret list
# Tail production logs in real time
npx wrangler tail
# Tail with filters
npx wrangler tail --status=error --search="timeout"
# View deployment versions
npx wrangler deployments list
# Rollback to a previous deployment
npx wrangler rollbackWrangler Configuration
# wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-09-01"
compatibility_flags = ["nodejs_compat"]
# Custom routes
routes = [
{ pattern = "api.example.com/*", zone_name = "example.com" }
]
# Or use a workers.dev subdomain
# workers_dev = true
# Environment variables (non-secret)
[vars]
ENVIRONMENT = "production"
API_VERSION = "v2"
# Staging environment override
[env.staging]
name = "my-api-staging"
routes = [
{ pattern = "api-staging.example.com/*", zone_name = "example.com" }
]
[env.staging.vars]
ENVIRONMENT = "staging"Worker Examples
Basic API Router
// src/index.ts
export interface Env {
ENVIRONMENT: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
switch (url.pathname) {
case "/":
return new Response("OK", { status: 200 });
case "/api/health":
return Response.json({
status: "healthy",
env: env.ENVIRONMENT,
timestamp: new Date().toISOString(),
});
case "/api/data":
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const body = await request.json();
// Process in the background after returning response
ctx.waitUntil(logToAnalytics(body));
return Response.json({ received: true });
default:
return new Response("Not Found", { status: 404 });
}
},
};
async function logToAnalytics(data: unknown): Promise<void> {
await fetch("https://analytics.example.com/ingest", {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
}Middleware: Rate Limiting with KV
// src/rate-limiter.ts
interface Env {
RATE_LIMIT_KV: KVNamespace;
ORIGIN_URL: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get("CF-Connecting-IP") || "unknown";
const key = `ratelimit:${ip}`;
const window = 60; // seconds
const maxRequests = 100;
const current = parseInt((await env.RATE_LIMIT_KV.get(key)) || "0");
if (current >= maxRequests) {
return new Response("Too Many Requests", {
status: 429,
headers: { "Retry-After": String(window) },
});
}
await env.RATE_LIMIT_KV.put(key, String(current + 1), {
expirationTtl: window,
});
// Forward to origin
const originRequest = new Request(env.ORIGIN_URL + new URL(request.url).pathname, request);
return fetch(originRequest);
},
};KV Storage Binding
# wrangler.toml
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456"
# Preview namespace for local dev
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456"
preview_id = "preview789"// KV operations in a Worker
interface Env {
MY_KV: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Write with TTL
await env.MY_KV.put("session:abc", JSON.stringify({ user: "alice" }), {
expirationTtl: 3600,
});
// Read
const session = await env.MY_KV.get("session:abc", "json");
// List keys by prefix
const list = await env.MY_KV.list({ prefix: "session:", limit: 100 });
// Delete
await env.MY_KV.delete("session:abc");
return Response.json({ session, keys: list.keys.length });
},
};# KV CLI operations
npx wrangler kv namespace create MY_KV
npx wrangler kv namespace list
npx wrangler kv key put --namespace-id=abc123 "config:feature-flags" '{"darkMode":true}'
npx wrangler kv key get --namespace-id=abc123 "config:feature-flags"
npx wrangler kv key list --namespace-id=abc123 --prefix="config:"D1 Database Binding
# wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-app"
database_id = "xxxx-yyyy-zzzz"// D1 SQL queries in a Worker
interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Parameterized query
const { results } = await env.DB.prepare(
"SELECT id, name, email FROM users WHERE active = ? LIMIT ?"
)
.bind(1, 50)
.all();
// Insert
await env.DB.prepare("INSERT INTO users (name, email) VALUES (?, ?)")
.bind("Alice", "alice@example.com")
.run();
// Batch multiple statements
await env.DB.batch([
env.DB.prepare("UPDATE users SET active = 0 WHERE last_login < ?").bind("2024-01-01"),
env.DB.prepare("DELETE FROM sessions WHERE expires_at < ?").bind(Date.now()),
]);
return Response.json(results);
},
};# D1 CLI operations
npx wrangler d1 create my-app
npx wrangler d1 list
npx wrangler d1 execute my-app --command="CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, active INTEGER DEFAULT 1)"
npx wrangler d1 execute my-app --file=./migrations/001_init.sql
npx wrangler d1 execute my-app --command="SELECT * FROM users" --jsonCron Triggers
# wrangler.toml
[triggers]
crons = [
"0 */6 * * *", # Every 6 hours
"0 0 * * MON", # Every Monday at midnight
"*/15 * * * *", # Every 15 minutes
]// src/index.ts — scheduled handler
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
switch (event.cron) {
case "0 */6 * * *":
ctx.waitUntil(cleanupExpiredSessions(env));
break;
case "0 0 * * MON":
ctx.waitUntil(generateWeeklyReport(env));
break;
}
},
async fetch(request: Request, env: Env): Promise<Response> {
return new Response("OK");
},
};Durable Objects
# wrangler.toml
[durable_objects]
bindings = [
{ name = "COUNTER", class_name = "Counter" }
]
[[migrations]]
tag = "v1"
new_classes = ["Counter"]// src/counter.ts — Durable Object class
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch(request: Request): Promise<Response> {
let count = (await this.state.storage.get<number>("count")) || 0;
count++;
await this.state.storage.put("count", count);
return Response.json({ count });
}
}
// src/index.ts — route to Durable Object
interface Env {
COUNTER: DurableObjectNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env.COUNTER.idFromName("global-counter");
const stub = env.COUNTER.get(id);
return stub.fetch(request);
},
};Custom Routing
# Route to specific zones
routes = [
{ pattern = "api.example.com/v1/*", zone_name = "example.com" },
{ pattern = "api.example.com/v2/*", zone_name = "example.com" },
]
# Or use custom domains (automatic SSL)
# Dashboard: Workers > your-worker > Triggers > Custom DomainsTroubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Error 1101: Worker threw exception | Unhandled error in fetch handler | Wrap handler in try/catch; check wrangler tail for stack trace |
exceeded CPU time limit | Worker exceeds 10ms CPU (free) or 30s (paid) | Optimize code; offload work with ctx.waitUntil() |
| KV reads return stale data | KV is eventually consistent (~60s) | Use cacheTtl option or switch to Durable Objects for strong consistency |
wrangler dev binding errors | Local bindings not configured | Use --remote flag or configure preview_id in wrangler.toml |
| Secret not found in Worker | Secret set for wrong environment | Verify with wrangler secret list --env <env> |
| CORS errors from browser | Missing CORS headers in response | Add Access-Control-Allow-Origin headers; handle OPTIONS preflight |
| Route not matching | Pattern does not include /* suffix | Add /* to catch all paths: api.example.com/* |
Related Skills
- cloudflare-pages - Frontend deployments with Pages Functions
- cloudflare-r2 - Object storage at the edge
- cloudflare-zero-trust - Protect Worker endpoints with Access
Related skills
FAQ
What can Workers connect to?
KV, D1, and R2 storage bindings, plus secrets managed with wrangler secret put.
Does it support scheduled jobs?
Yes. It covers running cron jobs on a schedule without maintaining infrastructure.