skills/cloudflare-worker-dev/SKILL.md
Cloudflare Workers, KV, Durable Objects, and edge computing development. Use for serverless APIs, caching, rate limiting, real-time features. Activate on "Workers", "KV", "Durable Objects", "wrangler", "edge function", "Cloudflare". NOT for Cloudflare Pages configuration (use deployment docs), DNS management, or general CDN settings.
npx skillsauth add curiositech/windags-skills cloudflare-worker-devInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build high-performance edge APIs with Workers, KV for caching, and Durable Objects for real-time coordination.
Need real-time state consistency?
├── YES → Use Durable Objects
│ ├── Chat/collaboration → WebSocket pattern
│ ├── Counters/queues → Atomic operations
│ └── Session management → Per-user Objects
└── NO → Continue to storage decision
├── Need fast reads, eventual consistency OK?
│ ├── YES → Use KV
│ │ ├── Cache TTL < 1 hour → expirationTtl: 3600
│ │ ├── Config/sessions → expirationTtl: 86400
│ │ └── Long-term cache → expirationTtl: 604800
│ └── NO → Use external DB (D1/Postgres)
└── File storage needed?
└── YES → Use R2 (images, documents)
Geographic scope?
├── City-level (10km radius) → 4-character geohash
├── Metro area (50km radius) → 3-character geohash
├── Regional (100km radius) → 2-character geohash
└── Country-level → Use country code instead
Cache hit rate priority?
├── HIGH → Use 2-3 character geohash (broader cache sharing)
└── PRECISION → Use 4-5 character geohash (location accuracy)
Rate limit scope?
├── Per IP → Use CF-Connecting-IP header
├── Per user → Use auth token/user ID
├── Per API key → Use Authorization header
└── Global → Use fixed key
Time window?
├── Burst protection (< 1 min) → Use in-memory counter
├── Short term (1-60 min) → Use KV with TTL
└── Long term (> 1 hour) → Use external storage
Symptoms: Worker returns 1102 error code, logs show "CPU time limit exceeded" Detection Rule: If logs contain "exceeded CPU time" or status 1102 responses Root Cause: Synchronous operations blocking event loop (JSON parsing large payloads, complex loops) Fix:
console.time() to find bottlenecksresponse.json() → ReadableStreamawait scheduler.wait(0) every 1000 iterationsSymptoms: Stale data returned immediately after writes, cache "flapping" between values Detection Rule: If read-after-write returns old value or cache hit rate drops unexpectedly Root Cause: Reading KV immediately after write hits different edge nodes Fix:
await kv.put(key, data); return data;X-Cache-Status header to debug cache behaviorSymptoms: Users get inconsistent rate limit responses, some bypass limits under load Detection Rule: If rate limit counters drift from expected values or users report sporadic 429s Root Cause: Multiple concurrent requests increment counter simultaneously Fix:
Symptoms: Browser requests work in dev but fail in production, OPTIONS returns 404 Detection Rule: If seeing "CORS policy" errors in browser console or 404s on OPTIONS requests Root Cause: Missing OPTIONS handler in route logic Fix:
Symptoms: Cache updates don't happen, cleanup tasks never run, inconsistent state
Detection Rule: If cache miss rates increase or background metrics stop updating
Root Cause: Using await instead of ctx.waitUntil() for background work
Fix:
ctx.waitUntil()Scenario: API needs to cache meeting data by geographic region with sub-second response times.
Step 1: Service Selection Decision
Step 2: Implementation
async function getMeetings(lat: number, lng: number, env: Env, ctx: ExecutionContext) {
// 3-char geohash for ~150km cells (metro coverage)
const geohash = Geohash.encode(lat, lng, 3);
const cacheKey = `meetings:${geohash}`;
// Check cache first
const cached = await env.MEETING_CACHE.get(cacheKey, 'json');
if (cached) {
return { data: cached, source: 'cache', geohash };
}
// Cache miss - fetch fresh data
const meetings = await fetchFromAPI(lat, lng);
// Background cache update (non-blocking)
ctx.waitUntil(
env.MEETING_CACHE.put(cacheKey, JSON.stringify(meetings), {
expirationTtl: 3600, // 1 hour TTL for meeting data
metadata: { cachedAt: Date.now(), coords: `${lat},${lng}` }
})
);
return { data: meetings, source: 'api', geohash };
}
Expert vs Novice Decision Points:
Step 3: Rate Limiting Integration
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
// Check rate limit first (fail fast)
const rateCheck = await checkRateLimit(ip, env, { maxRequests: 100, windowSeconds: 3600 });
if (!rateCheck.allowed) {
return new Response('Rate limited', { status: 429 });
}
// Process request
const url = new URL(request.url);
const lat = parseFloat(url.searchParams.get('lat') || '0');
const lng = parseFloat(url.searchParams.get('lng') || '0');
const result = await getMeetings(lat, lng, env, ctx);
return new Response(JSON.stringify(result.data), {
headers: {
'Content-Type': 'application/json',
'X-Cache': result.source,
'X-Geohash': result.geohash,
'X-RateLimit-Remaining': rateCheck.remaining.toString()
}
});
}
};
Deployment Readiness Checklist:
wrangler secret put, no hardcoded values in wrangler.toml or codewrangler tail)ctx.waitUntil(), no blocking operations in response pathThis skill should NOT be used for:
static-site-deployment skill insteadcloudflare-dns-management skill insteadcloudflare-cdn-config skill insteadai-integration skill insteadbatch-processing skill with external storagebackground-job-processing skill insteaddatabase-optimization skill with dedicated DBemail-service-integration skill insteadDelegate when:
data-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.