preferences-cloudflare-wrangler-reference
Cloudflare wrangler comprehensive reference for Workers, D1, R2, and KV configuration. Load when working with Cloudflare deployment or wrangler.toml.
SKILL.md
Full skill instructions
Cloudflare wrangler comprehensive reference
Comprehensive configuration reference for Cloudflare Wrangler synthesized from production patterns.
See @~/.claude/skills/preferences-web-application-deployment/SKILL.md for deployment workflows and practical patterns.
Configuration schema
{
// JSON schema for IDE autocomplete and validation
"$schema": "node_modules/wrangler/config-schema.json"
}
Enable autocomplete and validation in your IDE by including the schema reference at the top of wrangler.jsonc.
Core configuration
Worker identification
{
// Worker name - used as deployment identifier
"name": "my-cloudflare-worker",
// Entry point for the worker
// Can be .ts, .js, or .mjs - build tools handle transpilation
"main": "src/index.ts"
}
Runtime compatibility
{
// Compatibility date - locks API behavior to specific date
// Use recent date to get latest features
"compatibility_date": "2025-04-10",
// Compatibility flags - enable specific runtime features
"compatibility_flags": [
"nodejs_compat" // Enable Node.js compatibility layer
]
}
Important: Update compatibility_date regularly to access new platform features.
Workers.dev subdomain
{
// Enable workers.dev subdomain for testing
// Set to false in production to avoid accidental exposure
"workers_dev": true
}
Static assets configuration
Serve static assets alongside worker logic (SPAs, SSR apps).
{
"assets": {
// Directory containing static assets to serve
"directory": ".output/public",
// Binding name to access assets in worker code
"binding": "ASSETS",
// Handle 404s as SPA - serve index.html for missing routes
"not_found_handling": "single-page-application",
// Patterns where worker runs before checking for static assets
// Useful for API routes, auth endpoints, or dynamic paths
"run_worker_first": ["/api/*", "/trpc/*", "/auth/*"]
}
}
Pattern: TanStack Start or similar SSR frameworks serve frontend assets while worker handles API routes.
Build configuration
{
"build": {
// Command to run before deployment
"command": "bun run build",
// Working directory for build command
"cwd": ".",
// Directories to watch during `wrangler dev`
"watch_dir": "src"
}
}
Alternative: Use framework-specific build tools (Vite, etc.) and skip wrangler build command.
Routing configuration
Custom domain routing
{
"routes": [
{
// Custom domain routing (requires DNS setup in Cloudflare)
"pattern": "example.com",
"custom_domain": true
},
{
// Zone-based routing with wildcard pattern
"zone_name": "example.com",
"pattern": "api.example.com/*"
}
]
}
Setup:
- Add domain to Cloudflare DNS
- Configure route pattern in wrangler.jsonc
- Deploy worker - route activates automatically
Observability
{
"observability": {
// Enable metrics, logs, and traces in Cloudflare dashboard
"enabled": true
}
}
Access observability data: Cloudflare Dashboard > Workers & Pages > [Your Worker] > Metrics
Smart Placement
Automatically places worker near users for optimal latency.
{
"placement": {
"mode": "smart"
}
}
When to use: Geographic user distribution with latency-sensitive workloads.
See: https://developers.cloudflare.com/workers/configuration/smart-placement/
Environment variables
Non-sensitive configuration (vars)
{
"vars": {
"API_URL": "https://api.example.com",
"FEATURE_FLAG_NEW_UI": "true",
"LOG_LEVEL": "info"
}
}
Access in worker:
import { env } from "cloudflare:workers";
const apiUrl = env.API_URL;
const featureEnabled = env.FEATURE_FLAG_NEW_UI === "true";
Sensitive data (secrets)
Secrets not stored in wrangler.jsonc - managed via CLI:
# Set secret
wrangler secret put DATABASE_PASSWORD
# List secrets
wrangler secret list
# Delete secret
wrangler secret delete OLD_SECRET
# Set secret for specific environment
wrangler secret put API_KEY --env production
Access in worker (same as vars):
import { env } from "cloudflare:workers";
const dbPassword = env.DATABASE_PASSWORD;
Cloudflare bindings
Connect to Cloudflare platform resources.
See: https://developers.cloudflare.com/workers/runtime-apis/bindings/
D1 databases (serverless SQL)
{
"d1_databases": [
{
"binding": "DB", // Access via env.DB in worker code
"database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"database_name": "my-app-db",
"experimental_remote": true // Allow remote access during local dev
}
]
}
Usage:
import { drizzle } from "drizzle-orm/d1";
const db = drizzle(env.DB);
const users = await db.select().from(usersTable);
Create database:
wrangler d1 create my-app-db
KV namespaces (key-value storage)
{
"kv_namespaces": [
{
"binding": "CACHE",
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"experimental_remote": true
}
]
}
Usage:
// Write
await env.CACHE.put("key", "value", {
expirationTtl: 3600 // 1 hour
});
// Read
const value = await env.CACHE.get("key");
// Read as JSON
const data = await env.CACHE.get("key", "json");
// Delete
await env.CACHE.delete("key");
// List keys
const list = await env.CACHE.list();
Create namespace:
wrangler kv namespace create CACHE
R2 buckets (object storage)
{
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-storage-bucket"
}
]
}
Usage (S3-compatible API):
// Upload object
await env.BUCKET.put("uploads/file.pdf", fileData, {
httpMetadata: {
contentType: "application/pdf"
}
});
// Download object
const file = await env.BUCKET.get("uploads/file.pdf");
const blob = await file.blob();
// Delete object
await env.BUCKET.delete("uploads/file.pdf");
// List objects
const list = await env.BUCKET.list({ prefix: "uploads/" });
Create bucket:
wrangler r2 bucket create my-storage-bucket
Service bindings (worker-to-worker communication)
{
"services": [
{
"binding": "BACKEND_SERVICE",
"service": "data-service-production",
"experimental_remote": true // Enable service-to-service calls in dev
}
]
}
Usage:
// Call another worker service
const response = await env.BACKEND_SERVICE.fetch(
new Request("https://internal/api/data", {
method: "POST",
body: JSON.stringify({ query: "..." })
})
);
const data = await response.json();
Pattern: Microservices architecture with multiple workers.
apps/
user-application/ # Frontend worker
data-service/ # Backend API worker
auth-service/ # Authentication worker
Browser rendering (headless browser)
{
"browser": {
"binding": "VIRTUAL_BROWSER"
}
}
Usage:
const browser = await env.VIRTUAL_BROWSER.launch();
const page = await browser.newPage();
await page.goto("https://example.com");
// Take screenshot
const screenshot = await page.screenshot();
// Get page content
const html = await page.content();
await browser.close();
Use cases: Web scraping, PDF generation, automated testing, SEO previews.
Workers AI (AI model inference)
{
"ai": {
"binding": "AI"
}
}
Usage:
// Text generation
const response = await env.AI.run("@cf/meta/llama-2-7b-chat-int8", {
prompt: "What is the capital of France?"
});
// Image classification
const result = await env.AI.run("@cf/microsoft/resnet-50", {
image: imageData
});
// Text embeddings
const embeddings = await env.AI.run("@cf/baai/bge-base-en-v1.5", {
text: "Hello world"
});
See: https://developers.cloudflare.com/workers-ai/
Queues (async message processing)
{
"queues": {
"consumers": [
{
"queue": "data-processing-queue",
"dead_letter_queue": "data-processing-dlq" // Failed messages route here
},
{
"queue": "data-processing-dlq",
"retry_delay": 0 // Manual retry for DLQ messages
}
],
"producers": [
{
"binding": "QUEUE",
"queue": "data-processing-queue"
}
]
}
}
Producer usage:
// Send single message
await env.QUEUE.send({
userId: "123",
action: "process_upload"
});
// Batch send
await env.QUEUE.sendBatch([
{ body: { userId: "123" } },
{ body: { userId: "456" } }
]);
Consumer handler:
export default {
async queue(batch: MessageBatch, env: Env): Promise<void> {
for (const message of batch.messages) {
try {
await processMessage(message.body);
message.ack();
} catch (error) {
message.retry(); // Retry or send to DLQ
}
}
}
}
Create queue:
wrangler queues create data-processing-queue
wrangler queues create data-processing-dlq
Workflows (durable execution)
Long-running, durable processes that survive worker restarts.
{
"workflows": [
{
"binding": "MY_WORKFLOW",
"name": "my-workflow-production",
"class_name": "MyWorkflow" // Must match exported class in code
}
]
}
Workflow definition:
import { WorkflowEntrypoint, WorkflowStep } from "cloudflare:workers";
export class MyWorkflow extends WorkflowEntrypoint {
async run(event: any, step: WorkflowStep) {
// Steps are checkpointed - execution resumes on failure
const result1 = await step.do("fetch-data", async () => {
return await fetch("https://api.example.com/data");
});
// Sleep preserves state
await step.sleep("wait-for-processing", "1 hour");
const result2 = await step.do("process-data", async () => {
return processData(result1);
});
return result2;
}
}
Trigger workflow:
const instance = await env.MY_WORKFLOW.create({
params: { userId: "123" }
});
// Check status
const status = await instance.status();
Durable Objects (stateful compute)
Strongly consistent, stateful workers with persistent storage.
{
"durable_objects": {
"bindings": [
{
"name": "TRACKER_OBJECT",
"class_name": "EventTracker"
},
{
"name": "SCHEDULER_OBJECT",
"class_name": "TaskScheduler"
}
]
}
}
Durable Object migrations:
Required when adding or modifying Durable Objects.
{
"migrations": [
{
"tag": "v1",
"new_classes": ["TaskScheduler"] // Standard Durable Object
},
{
"tag": "v2",
"new_sqlite_classes": ["EventTracker"] // DO with SQLite storage
}
]
}
Durable Object definition:
import { DurableObject } from "cloudflare:workers";
export class EventTracker extends DurableObject {
async fetch(request: Request) {
// Strongly consistent storage
let count = (await this.ctx.storage.get<number>("count")) || 0;
count++;
await this.ctx.storage.put("count", count);
return new Response(count.toString());
}
// WebSocket connections
async webSocketMessage(ws: WebSocket, message: string) {
// Handle WebSocket messages
}
}
Access from worker:
// Create unique Durable Object instance
const id = env.TRACKER_OBJECT.idFromName("global-tracker");
const stub = env.TRACKER_OBJECT.get(id);
// Call Durable Object
const response = await stub.fetch(request);
Use cases: Chat rooms, collaborative editing, counters, rate limiting, session management.
Environment-specific configuration
Override any top-level setting per environment.
{
"name": "my-app",
// Default configuration (used for wrangler dev)
"vars": {
"API_URL": "https://api-dev.example.com",
"STRIPE_PRODUCT_ID": "price_dev_xxxxx"
},
"d1_databases": [
{
"binding": "DB",
"database_id": "dev-database-id",
"experimental_remote": true
}
],
// Environment-specific overrides
"env": {
"stage": {
"vars": {
"API_URL": "https://api-stage.example.com",
"STRIPE_PRODUCT_ID": "price_staging_xxxxx"
},
"routes": [
{
"custom_domain": true,
"pattern": "stage.example.com"
}
],
"d1_databases": [
{
"binding": "DB",
"database_id": "stage-database-id",
"experimental_remote": true
}
],
"kv_namespaces": [
{
"binding": "CACHE",
"id": "stage-kv-namespace-id"
}
],
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-storage-stage"
}
],
"services": [
{
"binding": "BACKEND_SERVICE",
"service": "data-service-stage",
"experimental_remote": true
}
],
"queues": {
"consumers": [
{
"queue": "data-queue-stage",
"dead_letter_queue": "data-dlq-stage"
}
],
"producers": [
{
"binding": "QUEUE",
"queue": "data-queue-stage"
}
]
}
},
"production": {
"vars": {
"API_URL": "https://api.example.com",
"STRIPE_PRODUCT_ID": "price_production_xxxxx"
},
"routes": [
{
"custom_domain": true,
"pattern": "example.com"
}
],
"d1_databases": [
{
"binding": "DB",
"database_id": "production-database-id"
}
],
"kv_namespaces": [
{
"binding": "CACHE",
"id": "production-kv-namespace-id"
}
],
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-storage-production"
}
],
"services": [
{
"binding": "BACKEND_SERVICE",
"service": "data-service-production"
}
],
"queues": {
"consumers": [
{
"queue": "data-queue-production",
"dead_letter_queue": "data-dlq-production"
}
],
"producers": [
{
"binding": "QUEUE",
"queue": "data-queue-production"
}
]
}
}
}
}
Deploy to environment:
# Deploy to staging
wrangler deploy --env stage
# Deploy to production
wrangler deploy --env production
# Local development (uses default config)
wrangler dev
Environment-specific secrets:
wrangler secret put DATABASE_PASSWORD --env stage
wrangler secret put DATABASE_PASSWORD --env production
Analytics Engine
Custom analytics with high-cardinality data.
{
"analytics_engine_datasets": [
{
"binding": "ANALYTICS"
}
]
}
Usage:
env.ANALYTICS.writeDataPoint({
// String dimensions
blobs: ["user_signup", "web"],
// Numeric values
doubles: [1, userId],
// Indexed fields for querying
indexes: [userId],
});
Query in dashboard: GraphQL API or SQL API for analytics queries.
Version metadata
{
"version_metadata": {
"binding": "CF_VERSION_METADATA"
}
}
Usage:
const metadata = env.CF_VERSION_METADATA;
console.log(`Version ID: ${metadata.id}`);
console.log(`Deployed at: ${metadata.timestamp}`);
Tail consumers
Process logs from other workers in real-time.
{
"tail_consumers": [
{
"service": "log-aggregator"
}
]
}
Log consumer worker:
export default {
async tail(events: TraceItem[], env: Env) {
for (const event of events) {
// Process logs, errors, traces
await env.DB.prepare(
"INSERT INTO logs (message, timestamp) VALUES (?, ?)"
).bind(event.logs[0].message, event.eventTimestamp).run();
}
}
}
Dispatch namespaces (Workers for Platforms)
Deploy user-provided workers dynamically.
{
"dispatch_namespaces": [
{
"binding": "DISPATCHER",
"namespace": "user-workers"
}
]
}
Usage:
// Deploy user worker
await env.DISPATCHER.put("user-123-worker", workerScript);
// Execute user worker
const worker = env.DISPATCHER.get("user-123-worker");
const response = await worker.fetch(request);
Use case: SaaS platforms allowing customers to deploy custom workers.
mTLS certificates
Mutual TLS for secure service-to-service communication.
{
"mtls_certificates": [
{
"binding": "MTLS_CERT",
"certificate_id": "cert-id"
}
]
}
Usage:
const response = await fetch("https://api.example.com", {
mtls: {
certificate_id: env.MTLS_CERT
}
});
Logpush integration
Stream logs to external destinations (S3, R2, HTTP).
{
"logpush": true
}
Configure logpush destinations in Cloudflare dashboard.
Rate limiting
{
"limits": {
"cpu_ms": 50
}
}
Set CPU time limits per request (default 10ms on free tier, 50ms on paid).
Cron triggers (scheduled events)
{
"triggers": {
"crons": [
"0 0 * * *" // Daily at midnight UTC
]
}
}
Handler:
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
// Run scheduled task
await cleanupExpiredData(env.DB);
}
}
Additional resources
- Wrangler CLI reference: https://developers.cloudflare.com/workers/wrangler/
- Bindings documentation: https://developers.cloudflare.com/workers/runtime-apis/bindings/
- Configuration schema: https://developers.cloudflare.com/workers/wrangler/configuration/
- Platform limits: https://developers.cloudflare.com/workers/platform/limits/
