Cloudflare Workers serverless platform for edge computing with workerd runtime, Wrangler CLI, Miniflare, Rust SDK (workers-rs), and official templates
Comprehensive assistance with Cloudflare Workers development, including the workerd runtime, Wrangler CLI, Miniflare local development, C3 project scaffolding, Rust SDK (workers-rs), and official starter templates.
This skill should be triggered when:
# Interactive project creation
npm create cloudflare@latest
# Create with specific framework
npm create cloudflare@latest my-app -- --framework=next
npm create cloudflare@latest my-api -- --framework=hono
# Accept defaults (quick start)
npm create cloudflare@latest my-worker -- -y
# TypeScript or JavaScript
npm create cloudflare@latest my-worker -- --lang=ts
npm create cloudflare@latest my-worker -- --lang=js
C3 supports: Angular, Astro, Docusaurus, Gatsby, Hono, Next.js, Nuxt, Qwik, React, Remix, SolidStart, SvelteKit, Vue
| Option | Description |
|--------|-------------|
| --framework | Specify web framework (next, react, svelte, etc.) |
| --type | hello-world, hello-world-durable-object, scheduled, queues, openapi |
| --category | hello-world, web-framework, demo, remote-template |
| --template | Use external Git-hosted template |
| --lang | ts, js, or python |
| --deploy | Enable/disable automatic deployment (default: true) |
| --git | Initialize Git repository (default: true) |
| -y | Accept all defaults |
# Login to Cloudflare (opens browser)
wrangler login
# Logout
wrangler logout
# Check current user
wrangler whoami
# Start local dev server with hot reload
wrangler dev
# Dev with specific config
wrangler dev --config wrangler.staging.toml
# Dev on specific port
wrangler dev --port 3000
# Local mode (no Cloudflare connection)
wrangler dev --local
# Deploy to Cloudflare
wrangler deploy
# Deploy to specific environment
wrangler deploy --env staging
# Deploy with dry run
wrangler deploy --dry-run
# Delete a Worker
wrangler delete
# Delete from specific environment
wrangler delete --env staging
# Upload version without deploying
wrangler versions upload
# Deploy version with gradual rollout
wrangler versions deploy
# List deployments
wrangler deployments list
# View deployment status
wrangler deployments status
# Rollback to previous version
wrangler rollback
# Stream real-time logs
wrangler tail
# Filter by HTTP status
wrangler tail --status 500
# Filter by HTTP method
wrangler tail --method POST
# Filter by IP address
wrangler tail --ip-address 192.168.1.1
# Sample rate (0-1)
wrangler tail --sampling-rate 0.1
# Create namespace
wrangler kv namespace create MY_KV
# List namespaces
wrangler kv namespace list
# Put a value
wrangler kv key put --binding=MY_KV "key" "value"
# Get a value
wrangler kv key get --binding=MY_KV "key"
# List keys
wrangler kv key list --binding=MY_KV
# Delete key
wrangler kv key delete --binding=MY_KV "key"
# Bulk operations (from JSON file)
wrangler kv bulk put --binding=MY_KV data.json
wrangler kv bulk delete --binding=MY_KV keys.json
# Create bucket
wrangler r2 bucket create my-bucket
# List buckets
wrangler r2 bucket list
# Upload object
wrangler r2 object put my-bucket/path/file.txt --file=./local-file.txt
# Download object
wrangler r2 object get my-bucket/path/file.txt
# Delete object
wrangler r2 object delete my-bucket/path/file.txt
# Configure CORS
wrangler r2 bucket cors set my-bucket --rules='[{"origins":["*"]}]'
# Custom domain
wrangler r2 bucket domain add my-bucket my-cdn.example.com
# Create database
wrangler d1 create my-database
# List databases
wrangler d1 list
# Execute SQL
wrangler d1 execute my-database --command="SELECT * FROM users"
# Execute from file
wrangler d1 execute my-database --file=./schema.sql
# Create migration
wrangler d1 migrations create my-database migration-name
# Apply migrations
wrangler d1 migrations apply my-database
# Time travel (point-in-time recovery)
wrangler d1 time-travel info my-database
wrangler d1 time-travel restore my-database --timestamp=2024-01-01T00:00:00Z
# Add secret (interactive prompt)
wrangler secret put MY_SECRET
# Delete secret
wrangler secret delete MY_SECRET
# List secrets
wrangler secret list
# Bulk upload from file
wrangler secret bulk secrets.json
# Create queue
wrangler queues create my-queue
# List queues
wrangler queues list
# Add consumer
wrangler queues consumer add my-queue my-worker
# Generate types from wrangler.toml bindings
wrangler types
# Include runtime types
wrangler types --include-runtime
| Option | Description |
|--------|-------------|
| --config | Specify custom config file |
| --env | Target specific environment |
| --cwd | Run from different directory |
| --env-file | Load environment variables from file |
| --help | Show command help |
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
# Enable workers.dev subdomain
workers_dev = true
# Account ID (or use CLOUDFLARE_ACCOUNT_ID env var)
account_id = "your-account-id"
# Single route
route = { pattern = "example.com/*", zone_name = "example.com" }
# Multiple routes
routes = [
{ pattern = "example.com/api/*", zone_name = "example.com" },
{ pattern = "api.example.com/*", zone_name = "example.com" }
]
# Custom domain
routes = [
{ pattern = "api.example.com", custom_domain = true }
]
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123..."
# Preview namespace (for wrangler dev)
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123..."
preview_id = "def456..."
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "abc123..."
[durable_objects]
bindings = [
{ name = "MY_DO", class_name = "MyDurableObject" }
]
[[migrations]]
tag = "v1"
new_classes = ["MyDurableObject"]
# Producer
[[queues.producers]]
binding = "MY_QUEUE"
queue = "my-queue"
# Consumer
[[queues.consumers]]
queue = "my-queue"
max_batch_size = 10
max_batch_timeout = 30
[vars]
API_URL = "https://api.example.com"
DEBUG = "false"
[triggers]
crons = [
"0 * * * *", # Every hour
"0 0 * * *", # Daily at midnight
"*/5 * * * *" # Every 5 minutes
]
[dev]
ip = "localhost"
port = 8787
local_protocol = "http"
# Staging environment
[env.staging]
name = "my-worker-staging"
vars = { DEBUG = "true" }
[env.staging.routes]
pattern = "staging.example.com/*"
zone_name = "example.com"
# Production environment
[env.production]
name = "my-worker-production"
vars = { DEBUG = "false" }
[limits]
cpu_ms = 50
[observability]
enabled = true
[observability.logs]
enabled = true
invocation_logs = true
head_sampling_rate = 1
Miniflare is a local simulator for Cloudflare Workers using the workerd runtime.
npm install miniflare --save-dev
import { Miniflare } from "miniflare";
const mf = new Miniflare({
script: `
export default {
async fetch(request, env) {
return new Response("Hello from Miniflare!");
}
}
`,
modules: true,
});
// Dispatch a request
const response = await mf.dispatchFetch("http://localhost/");
console.log(await response.text());
// Cleanup
await mf.dispose();
const mf = new Miniflare({
scriptPath: "./src/index.ts",
modules: true,
kvNamespaces: ["MY_KV"],
r2Buckets: ["MY_BUCKET"],
d1Databases: ["DB"],
durableObjects: {
MY_DO: "MyDurableObject"
},
bindings: {
API_KEY: "secret-key"
}
});
// Access bindings
const bindings = await mf.getBindings();
await bindings.MY_KV.put("key", "value");
const mf = new Miniflare({
scriptPath: "./src/index.ts",
modules: true,
kvPersist: true, // Persist to .mf/kv
r2Persist: "./data/r2", // Custom path
d1Persist: true,
cachePersist: true,
});
const mf = new Miniflare({
workers: [
{
name: "api",
scriptPath: "./api/index.ts",
modules: true,
routes: ["*/api/*"]
},
{
name: "frontend",
scriptPath: "./frontend/index.ts",
modules: true,
routes: ["*"]
}
]
});
const mf = new Miniflare({
scriptPath: "./src/index.ts",
modules: true,
inspectorPort: 9229, // Connect Chrome DevTools
verbose: true, // Detailed logging
});
| Method | Description |
|--------|-------------|
| dispatchFetch(url, init?) | Send HTTP request to worker |
| getBindings() | Get all worker bindings |
| getWorker(name?) | Get specific worker instance |
| getCaches() | Get CacheStorage instance |
| getD1Database(binding) | Get D1 database instance |
| dispose() | Cleanup and shutdown |
export default {
async fetch(request, env, ctx) {
return new Response('Hello World!', {
headers: { 'Content-Type': 'text/plain' }
});
}
};
export default {
async fetch(request, env, ctx) {
const data = { message: 'Hello from Workers', timestamp: Date.now() };
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
}
};
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
switch (url.pathname) {
case '/api/users':
return handleUsers(request, env);
case '/api/posts':
return handlePosts(request, env);
default:
return new Response('Not Found', { status: 404 });
}
}
};
export default {
async fetch(request, env, ctx) {
switch (request.method) {
case 'GET':
return handleGet(request, env);
case 'POST':
const body = await request.json();
return handlePost(body, env);
case 'PUT':
return handlePut(request, env);
case 'DELETE':
return handleDelete(request, env);
default:
return new Response('Method Not Allowed', { status: 405 });
}
}
};
export default {
async fetch(request, env, ctx) {
// Write to KV
await env.MY_KV.put('key', 'value', { expirationTtl: 3600 });
// Read from KV
const value = await env.MY_KV.get('key');
// Read as JSON
const data = await env.MY_KV.get('data', { type: 'json' });
// Delete from KV
await env.MY_KV.delete('key');
// List keys
const list = await env.MY_KV.list({ prefix: 'user:' });
return new Response(value);
}
};
export default {
async fetch(request, env, ctx) {
// Query
const { results } = await env.DB.prepare(
"SELECT * FROM users WHERE id = ?"
).bind(1).all();
// Insert
await env.DB.prepare(
"INSERT INTO users (name, email) VALUES (?, ?)"
).bind("John", "john@example.com").run();
// Batch
const batch = await env.DB.batch([
env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Alice"),
env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Bob"),
]);
return Response.json(results);
}
};
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const key = url.pathname.slice(1);
switch (request.method) {
case 'PUT':
await env.MY_BUCKET.put(key, request.body, {
httpMetadata: {
contentType: request.headers.get('Content-Type')
}
});
return new Response('Uploaded');
case 'GET':
const object = await env.MY_BUCKET.get(key);
if (!object) return new Response('Not Found', { status: 404 });
return new Response(object.body, {
headers: { 'Content-Type': object.httpMetadata.contentType }
});
case 'DELETE':
await env.MY_BUCKET.delete(key);
return new Response('Deleted');
}
}
};
// Worker entry point
export default {
async fetch(request, env, ctx) {
const id = env.COUNTER.idFromName('global');
const stub = env.COUNTER.get(id);
return stub.fetch(request);
}
};
// Durable Object class
export class Counter {
constructor(state, env) {
this.state = state;
}
async fetch(request) {
let count = (await this.state.storage.get('count')) || 0;
if (request.method === 'POST') {
count++;
await this.state.storage.put('count', count);
}
return Response.json({ count });
}
}
// Producer
export default {
async fetch(request, env, ctx) {
await env.MY_QUEUE.send({
type: 'email',
to: 'user@example.com',
subject: 'Hello'
});
return new Response('Queued');
}
};
// Consumer
export default {
async queue(batch, env, ctx) {
for (const message of batch.messages) {
console.log('Processing:', message.body);
// Process message
message.ack();
}
}
};
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
export default {
async fetch(request, env, ctx) {
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
const response = await handleRequest(request, env);
return new Response(response.body, {
...response,
headers: { ...response.headers, ...corsHeaders }
});
}
};
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
// Try cache first
let response = await cache.match(request);
if (!response) {
// Cache miss - fetch from origin
response = await fetch(request);
// Cache for 1 hour
response = new Response(response.body, response);
response.headers.set('Cache-Control', 'max-age=3600');
ctx.waitUntil(cache.put(request, response.clone()));
}
return response;
}
};
export default {
async scheduled(event, env, ctx) {
console.log('Cron executed at:', new Date(event.scheduledTime));
// Perform background task
await env.MY_KV.put('last_run', event.scheduledTime.toString());
// Make external API call
await fetch('https://api.example.com/webhook', {
method: 'POST',
body: JSON.stringify({ timestamp: event.scheduledTime })
});
}
};
export default {
async fetch(request, env, ctx) {
try {
const data = await env.MY_KV.get('key');
if (!data) {
return Response.json(
{ error: 'Not Found' },
{ status: 404 }
);
}
return Response.json({ data });
} catch (error) {
console.error('Worker error:', error);
return Response.json(
{ error: 'Internal Server Error' },
{ status: 500 }
);
}
}
};
export default {
async fetch(request, env, ctx) {
// Access variables from wrangler.toml [vars]
const apiUrl = env.API_URL;
// Access secrets (wrangler secret put)
const apiKey = env.API_KEY;
const response = await fetch(`${apiUrl}/data`, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
return response;
}
};
# Generate types from wrangler.toml
wrangler types
# Output to custom file
wrangler types --outdir ./types
// Auto-generated by wrangler types
interface Env {
MY_KV: KVNamespace;
MY_BUCKET: R2Bucket;
DB: D1Database;
MY_DO: DurableObjectNamespace;
API_KEY: string;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
const value = await env.MY_KV.get('key');
return new Response(value);
}
};
Cloudflare provides production-ready templates at github.com/cloudflare/templates.
# Clone any template with C3
npm create cloudflare@latest my-app -- --template cloudflare/templates/<template-name>
# Examples
npm create cloudflare@latest my-db -- --template cloudflare/templates/d1-template
npm create cloudflare@latest my-chat -- --template cloudflare/templates/durable-chat-template
npm create cloudflare@latest my-ai -- --template cloudflare/templates/llm-chat-app-template
| Template | Description |
|----------|-------------|
| next-starter-template | Next.js starter application |
| remix-starter-template | Remix framework starter |
| astro-blog-starter-template | Blog platform using Astro |
| vite-react-template | Vite with React configuration |
| react-router-starter-template | React Router basic setup |
| react-router-hono-fullstack-template | Full-stack React Router + Hono backend |
| react-router-postgres-ssr-template | SSR with PostgreSQL integration |
| react-postgres-fullstack-template | React frontend with PostgreSQL backend |
| Template | Description |
|----------|-------------|
| d1-template | D1 SQL database starter with migrations |
| d1-starter-sessions-api-template | Session management using D1 |
| to-do-list-kv-template | Key-Value storage demonstration |
| postgres-hyperdrive-template | PostgreSQL via Hyperdrive connection pooling |
| mysql-hyperdrive-template | MySQL via Hyperdrive |
| r2-explorer-template | R2 object storage interface |
| Template | Description |
|----------|-------------|
| durable-chat-template | Real-time chat with WebSockets and PartyKit |
| hello-world-do-template | Durable Objects introduction |
| multiplayer-globe-template | Real-time collaborative mapping |
| Template | Description |
|----------|-------------|
| llm-chat-app-template | AI chatbot with Workers AI streaming |
| text-to-image-template | Image generation with Workers AI |
| Template | Description |
|----------|-------------|
| saas-admin-template | SaaS dashboard with Astro, D1, and Workflows |
| openauth-template | Authentication implementation |
| workers-for-platforms-template | Platform-as-a-service architecture |
| Template | Description |
|----------|-------------|
| workflows-starter-template | Workflow orchestration with Durable Objects |
| microfrontend-template | Micro frontend architecture |
| containers-template | Containerized deployment |
| chanfana-openapi-template | OpenAPI documentation generation |
| worker-publisher-template | Publishing service pattern |
// Query D1 database
export default {
async fetch(request, env) {
const { results } = await env.DB.prepare(
"SELECT * FROM comments LIMIT 3"
).all();
return Response.json(results);
}
};
Setup steps:
npm installwrangler d1 create d1-template-databasewrangler.json with database IDwrangler d1 migrations apply d1-template-databasewrangler deployUses PartyKit Server API with Durable Objects:
// Chat room Durable Object
export class ChatRoom {
connections = new Set();
async onConnect(connection, ctx) {
this.connections.add(connection);
// Load chat history from SQL storage
const history = await this.ctx.storage.sql.exec(
"SELECT * FROM messages ORDER BY timestamp DESC LIMIT 50"
);
connection.send(JSON.stringify({ type: 'history', messages: history }));
}
async onMessage(message, connection) {
// Store message
await this.ctx.storage.sql.exec(
"INSERT INTO messages (author, text, timestamp) VALUES (?, ?, ?)",
[message.author, message.text, Date.now()]
);
// Broadcast to all connections
for (const conn of this.connections) {
conn.send(JSON.stringify(message));
}
}
}
// Streaming AI responses with SSE
export default {
async fetch(request, env) {
if (request.method === 'POST' && new URL(request.url).pathname === '/api/chat') {
const { messages } = await request.json();
const stream = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
...messages
],
stream: true
});
return new Response(stream, {
headers: { 'Content-Type': 'text/event-stream' }
});
}
}
};
Customization points:
MODEL_ID: Swap between Workers AI modelsSYSTEM_PROMPT: Adjust AI behavior// Define a workflow with steps
export class MyWorkflow extends WorkflowEntrypoint {
async run(event, step) {
// Step 1: Process input
const processed = await step.do('process', async () => {
return processData(event.payload);
});
// Step 2: Wait for time or event
await step.sleep('wait', '1 hour');
// Step 3: Continue processing
const result = await step.do('finalize', async () => {
return finalizeData(processed);
});
return result;
}
}
Features:
Stack: Astro + Shadcn UI + D1 + Workflows
// API with token authentication
export default {
async fetch(request, env) {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (token !== env.API_TOKEN) {
return Response.json({ error: 'Unauthorized' }
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer