
Cloudflare Workers Dev Experience
- 220 installs
- 202 repo stars
- Updated August 4, 2026
- secondsky/claude-skills
Use cloudflare-workers-dev-experience for development tasks
About
cloudflare-workers-dev-experience: A skill for development. This provides functionality for development workflows.
- cloudflare-workers-dev-experience
Cloudflare Workers Dev Experience by the numbers
- 220 all-time installs (skills.sh)
- +12 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,809 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/secondsky/claude-skills --skill cloudflare-workers-dev-experienceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 220 |
|---|---|
| repo stars | ★ 202 |
| Last updated | August 4, 2026 |
| Repository | secondsky/claude-skills ↗ |
What it does
Use cloudflare-workers-dev-experience for development tasks
Files
Cloudflare Workers Developer Experience
Local development setup with Wrangler, Miniflare, and modern tooling.
Quick Start
# Create new project
bunx create-cloudflare@latest my-worker
# Or from scratch
mkdir my-worker && cd my-worker
bun init -y
bun add -d wrangler @cloudflare/workers-types
# Start local development
bunx wrangler devSecure Installation
Scaffolding tools like bunx create-cloudflare download and execute remote code. Before running, follow supply chain security best practices:
- Block post-install scripts —
npm config set ignore-scripts true(or Bun: disabled by default) - Cooldown period — Wait 7 days for new package versions to be vetted by the community
- Audit before installing — Run
socket package score npm <pkg>or usesocket npm install <pkg>to check packages
Load the dependency-upgrade skill for full security configuration including Socket CLI integration, cooldown setup, lockfile validation, and CI enforcement.
Essential wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2024-12-01",
// Development settings
"dev": {
"port": 8787,
"local_protocol": "http"
},
// Environment variables (non-secret)
"vars": {
"ENVIRONMENT": "development"
},
// Bindings
"kv_namespaces": [
{ "binding": "KV", "id": "abc123", "preview_id": "def456" }
],
"d1_databases": [
{ "binding": "DB", "database_id": "xyz789", "database_name": "my-db" }
],
"r2_buckets": [
{ "binding": "BUCKET", "bucket_name": "my-bucket" }
]
}Critical Rules
1. Always use `wrangler dev` for local testing - Simulates Cloudflare runtime accurately 2. Set `compatibility_date` - Controls runtime behavior, update quarterly 3. Use preview IDs for local dev - Separate from production bindings 4. Configure TypeScript properly - Use @cloudflare/workers-types 5. Enable source maps - Better error stacks in development
Top 6 Errors Prevented
| Error | Symptom | Prevention |
|---|---|---|
| Module not found | Import errors on deploy | Set "moduleResolution": "bundler" in tsconfig |
| Binding undefined | env.KV is undefined locally | Add preview_id to KV namespace config |
| HMR not working | Changes not reflecting | Check port conflicts, use --local flag |
| D1 schema mismatch | Queries fail locally | Run migrations on local DB |
| Type errors | Missing binding types | Generate types with wrangler types |
| CORS issues | Browser blocking requests | Add CORS headers in dev handler |
Local Development Workflow
# Start dev server (recommended)
bunx wrangler dev
# With live reload
bunx wrangler dev --live-reload
# Remote mode (use actual Cloudflare services)
bunx wrangler dev --remote
# Specify environment
bunx wrangler dev --env staging
# Custom port
bunx wrangler dev --port 3000TypeScript Configuration
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"types": ["@cloudflare/workers-types"],
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}Package.json Scripts
{
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"deploy:staging": "wrangler deploy --env staging",
"deploy:production": "wrangler deploy --env production",
"test": "vitest",
"test:watch": "vitest --watch",
"type-check": "tsc --noEmit",
"lint": "eslint src/",
"types": "wrangler types",
"tail": "wrangler tail",
"db:migrate": "wrangler d1 migrations apply DB",
"db:studio": "wrangler d1 execute DB --local --command 'SELECT 1'"
}
}Debugging
Console Logging
// Development-only logging
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (env.ENVIRONMENT === 'development') {
console.log('Request:', request.method, request.url);
console.log('Headers:', Object.fromEntries(request.headers));
}
// Handler logic...
}
};Using wrangler tail
# Real-time logs from deployed worker
wrangler tail
# Filter by status
wrangler tail --status error
# Filter by method
wrangler tail --method POST
# JSON format for parsing
wrangler tail --format jsonVS Code Debugging
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Wrangler Dev",
"type": "node",
"request": "launch",
"runtimeExecutable": "bunx",
"runtimeArgs": ["wrangler", "dev", "--inspector-port", "9229"],
"skipFiles": ["<node_internals>/**"],
"sourceMaps": true
}
]
}When to Load References
Load specific references based on the task:
- Setting up project? → Load
references/local-development.mdfor complete setup guide - Configuring wrangler? → Load
references/wrangler-config.mdfor all configuration options - Debugging issues? → Load
references/debugging-tools.mdfor debugging techniques
Templates
| Template | Purpose | Use When |
|---|---|---|
templates/wrangler-config.jsonc | Complete wrangler config | Starting new project |
templates/dev-script.ts | Development utilities | Adding dev helpers |
Scripts
| Script | Purpose | Command |
|---|---|---|
scripts/dev-setup.sh | Initialize dev environment | ./dev-setup.sh |
Resources
- Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/
- Configuration: https://developers.cloudflare.com/workers/wrangler/configuration/
- Local Development: https://developers.cloudflare.com/workers/testing/local-development/
Debugging Tools for Cloudflare Workers
Techniques and tools for debugging Workers locally and in production.
Console Logging
Structured Logging
interface LogContext {
requestId: string;
method: string;
path: string;
[key: string]: unknown;
}
function log(level: string, message: string, ctx: LogContext) {
console.log(JSON.stringify({
level,
message,
timestamp: Date.now(),
...ctx,
}));
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const requestId = crypto.randomUUID();
const url = new URL(request.url);
const ctx: LogContext = {
requestId,
method: request.method,
path: url.pathname,
};
log('info', 'Request received', ctx);
try {
const response = await handleRequest(request, env);
log('info', 'Request completed', { ...ctx, status: response.status });
return response;
} catch (error) {
log('error', 'Request failed', {
...ctx,
error: (error as Error).message,
stack: (error as Error).stack,
});
throw error;
}
}
};Conditional Logging
interface Env {
ENVIRONMENT: string;
DEBUG: string;
}
function createLogger(env: Env) {
const isDebug = env.DEBUG === 'true' || env.ENVIRONMENT === 'development';
return {
debug: (...args: unknown[]) => {
if (isDebug) console.log('[DEBUG]', ...args);
},
info: (...args: unknown[]) => console.log('[INFO]', ...args),
warn: (...args: unknown[]) => console.warn('[WARN]', ...args),
error: (...args: unknown[]) => console.error('[ERROR]', ...args),
};
}Wrangler Tail
Real-time logs from deployed workers:
# Basic tail
bunx wrangler tail
# Filter by status
bunx wrangler tail --status error
bunx wrangler tail --status ok
# Filter by method
bunx wrangler tail --method POST
# Filter by path
bunx wrangler tail --search "/api/users"
# JSON output for parsing
bunx wrangler tail --format json
# Specific environment
bunx wrangler tail --env production
# With IP addresses
bunx wrangler tail --ipParsing Tail Output
# Filter errors with jq
bunx wrangler tail --format json | jq 'select(.outcome == "exception")'
# Count by status
bunx wrangler tail --format json | jq '.event.response.status' | sort | uniq -c
# Save to file
bunx wrangler tail --format json > logs.jsonChrome DevTools
Enable Inspector
# Start with inspector enabled
bunx wrangler dev --inspector-port 9229Connect Chrome DevTools
1. Open chrome://inspect in Chrome 2. Click "Configure..." and add localhost:9229 3. Click "inspect" on your worker 4. Use Sources tab for breakpoints
VS Code Debugging
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Worker",
"type": "node",
"request": "attach",
"port": 9229,
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/**"
]
}
]
}Then: 1. Run bunx wrangler dev --inspector-port 9229 2. Press F5 in VS Code 3. Set breakpoints in your code
Error Tracking
Error Boundary Pattern
async function withErrorTracking<T>(
fn: () => Promise<T>,
context: object
): Promise<T> {
try {
return await fn();
} catch (error) {
console.error(JSON.stringify({
level: 'error',
error: {
name: (error as Error).name,
message: (error as Error).message,
stack: (error as Error).stack,
},
context,
timestamp: Date.now(),
}));
throw error;
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return withErrorTracking(
() => handleRequest(request, env),
{
url: request.url,
method: request.method,
}
);
}
};External Error Tracking
// Sentry integration example
async function reportToSentry(error: Error, context: object): Promise<void> {
await fetch('https://sentry.io/api/xxx/envelope/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-sentry-envelope',
},
body: JSON.stringify({
event_id: crypto.randomUUID(),
exception: {
values: [{
type: error.name,
value: error.message,
stacktrace: error.stack,
}],
},
extra: context,
}),
});
}Request/Response Debugging
Request Inspector
function inspectRequest(request: Request): object {
return {
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers),
cf: request.cf, // Cloudflare-specific info
};
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
console.log('Request:', JSON.stringify(inspectRequest(request), null, 2));
const response = await handleRequest(request, env);
console.log('Response:', {
status: response.status,
headers: Object.fromEntries(response.headers),
});
return response;
}
};Response Timing
async function timeRequest<T>(
name: string,
fn: () => Promise<T>
): Promise<T> {
const start = Date.now();
try {
const result = await fn();
console.log(`[TIMING] ${name}: ${Date.now() - start}ms`);
return result;
} catch (error) {
console.log(`[TIMING] ${name}: ${Date.now() - start}ms (failed)`);
throw error;
}
}
// Usage
const data = await timeRequest('database', () => db.query('SELECT...'));
const external = await timeRequest('api', () => fetch('https://api.com'));Memory Debugging
Heap Usage (Development)
// Check memory in development
if (typeof process !== 'undefined' && process.memoryUsage) {
const usage = process.memoryUsage();
console.log('Memory:', {
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
});
}Large Object Detection
function estimateSize(obj: unknown): number {
const seen = new WeakSet();
function size(value: unknown): number {
if (value === null) return 4;
if (typeof value === 'boolean') return 4;
if (typeof value === 'number') return 8;
if (typeof value === 'string') return value.length * 2;
if (typeof value === 'object') {
if (seen.has(value)) return 0;
seen.add(value);
if (Array.isArray(value)) {
return value.reduce((acc, item) => acc + size(item), 0);
}
return Object.entries(value).reduce(
(acc, [key, val]) => acc + key.length * 2 + size(val),
0
);
}
return 0;
}
return size(obj);
}
// Warn if object is large
function checkSize(name: string, obj: unknown, maxKB: number = 100): void {
const sizeKB = estimateSize(obj) / 1024;
if (sizeKB > maxKB) {
console.warn(`[MEMORY] ${name} is ${sizeKB.toFixed(1)}KB (limit: ${maxKB}KB)`);
}
}Performance Profiling
Request Timing
class Timer {
private marks: Map<string, number> = new Map();
mark(name: string): void {
this.marks.set(name, Date.now());
}
measure(name: string, startMark: string): number {
const start = this.marks.get(startMark);
if (!start) throw new Error(`Mark ${startMark} not found`);
return Date.now() - start;
}
getTimings(): object {
const entries: Record<string, number> = {};
let previous = 0;
for (const [name, time] of this.marks) {
entries[name] = previous ? time - previous : 0;
previous = time;
}
return entries;
}
}
// Usage
const timer = new Timer();
timer.mark('start');
await step1();
timer.mark('step1');
await step2();
timer.mark('step2');
console.log('Timings:', timer.getTimings());Debug Headers
Add debug info to responses:
function addDebugHeaders(
response: Response,
debug: object
): Response {
const newResponse = new Response(response.body, response);
// Only add in development
if (process.env.NODE_ENV === 'development') {
newResponse.headers.set('X-Debug', JSON.stringify(debug));
}
return newResponse;
}Troubleshooting Checklist
1. Check console output - wrangler tail or dev server logs 2. Verify bindings - Ensure all bindings are configured 3. Check compatibility date - May affect runtime behavior 4. Inspect request/response - Log full request details 5. Use debugger - Chrome DevTools or VS Code 6. Check external services - Time and log external calls 7. Verify secrets - Ensure secrets are set for environment
Local Development for Cloudflare Workers
Complete guide for local development with Wrangler and Miniflare.
Getting Started
Project Setup
# Create new project (recommended)
bunx create-cloudflare@latest my-worker
cd my-worker
# Manual setup
mkdir my-worker && cd my-worker
bun init -y
bun add -d wrangler @cloudflare/workers-types typescriptProject Structure
my-worker/
├── src/
│ ├── index.ts # Main entry point
│ ├── routes/ # Route handlers
│ └── lib/ # Shared utilities
├── test/
│ └── index.test.ts # Tests
├── wrangler.jsonc # Wrangler configuration
├── tsconfig.json # TypeScript configuration
├── package.json
└── .dev.vars # Local secrets (gitignored)Wrangler Dev Server
Basic Usage
# Start local dev server
bunx wrangler dev
# Output:
# ⛅️ wrangler 3.x.x
# Your worker has access to the following bindings:
# - KV Namespaces:
# - KV: abc123
# ⎔ Starting local server...
# Ready on http://localhost:8787Command Options
# With live reload (automatic browser refresh)
bunx wrangler dev --live-reload
# Remote mode (use real Cloudflare services)
bunx wrangler dev --remote
# Specify environment
bunx wrangler dev --env staging
# Custom port
bunx wrangler dev --port 3000
# Custom host
bunx wrangler dev --ip 0.0.0.0
# Enable inspector for debugging
bunx wrangler dev --inspector-port 9229
# Persist local state between restarts
bunx wrangler dev --persist
# Specify config file
bunx wrangler dev --config wrangler.dev.jsoncLocal vs Remote Mode
| Feature | Local (default) | Remote (--remote) |
|---|---|---|
| Speed | Faster | Slower |
| KV | Simulated | Real Cloudflare KV |
| D1 | Local SQLite | Real D1 database |
| R2 | Local filesystem | Real R2 bucket |
| Durable Objects | Simulated | Real DOs |
| Network | localhost | Cloudflare edge |
Local Secrets
Create .dev.vars for local secrets (automatically loaded):
# .dev.vars (gitignored)
API_KEY=dev-api-key-123
DATABASE_URL=postgres://localhost:5432/dev
STRIPE_SECRET_KEY=sk_test_xxxAccess in code:
interface Env {
API_KEY: string;
DATABASE_URL: string;
STRIPE_SECRET_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// env.API_KEY is available
return new Response('OK');
}
};Binding Simulation
KV Namespace
// wrangler.jsonc
{
"kv_namespaces": [
{
"binding": "KV",
"id": "production-id",
"preview_id": "preview-id" // Used in dev
}
]
}Local KV data persists in .wrangler/state/.
D1 Database
// wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_id": "production-db-id",
"database_name": "my-database"
}
]
}# Apply migrations locally
bunx wrangler d1 migrations apply DB --local
# Execute SQL locally
bunx wrangler d1 execute DB --local --command "SELECT * FROM users"
# Open D1 console
bunx wrangler d1 execute DB --local --command ".tables"R2 Bucket
// wrangler.jsonc
{
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-bucket",
"preview_bucket_name": "my-bucket-preview"
}
]
}Local R2 data persists in .wrangler/state/.
Durable Objects
// wrangler.jsonc
{
"durable_objects": {
"bindings": [
{
"name": "ROOMS",
"class_name": "ChatRoom"
}
]
}
}Hot Module Replacement
Wrangler automatically rebuilds and restarts on file changes:
# Watch mode is default
bunx wrangler dev
# Enable browser live reload
bunx wrangler dev --live-reloadTroubleshooting HMR
1. Changes not reflecting:
- Check for port conflicts
- Ensure file is being watched (check
src/path) - Try
--live-reloadflag
2. Slow rebuilds:
- Minimize dependencies
- Use
--persistto avoid re-initializing state
3. Build errors:
- Check console for TypeScript errors
- Verify imports are correct
Development Workflow
Recommended workflow:
# Terminal 1: Dev server
bunx wrangler dev
# Terminal 2: Type checking (watch)
bunx tsc --watch --noEmit
# Terminal 3: Tests (watch)
bunx vitest --watchpackage.json scripts:
{
"scripts": {
"dev": "wrangler dev",
"dev:remote": "wrangler dev --remote",
"dev:persist": "wrangler dev --persist",
"check": "tsc --noEmit",
"check:watch": "tsc --noEmit --watch"
}
}Environment Configuration
Multiple Environments
// wrangler.jsonc
{
"name": "my-worker",
// Base configuration
"vars": {
"ENVIRONMENT": "development"
},
// Environment-specific
"env": {
"staging": {
"name": "my-worker-staging",
"vars": {
"ENVIRONMENT": "staging"
}
},
"production": {
"name": "my-worker-production",
"vars": {
"ENVIRONMENT": "production"
}
}
}
}# Development (default)
bunx wrangler dev
# Staging
bunx wrangler dev --env staging
# Production (careful!)
bunx wrangler dev --env production --remotePersistence
By default, local state is ephemeral. Use --persist to keep state:
bunx wrangler dev --persist
# State stored in .wrangler/state/
# - v3/kv/
# - v3/d1/
# - v3/r2/Accessing from Other Devices
# Bind to all interfaces
bunx wrangler dev --ip 0.0.0.0
# Access from other devices on network
# http://192.168.1.x:8787Common Issues
Port Already in Use
# Use different port
bunx wrangler dev --port 3001
# Or kill existing process
lsof -i :8787 # Find PID
kill -9 <PID>Binding Not Available
Ensure binding has preview_id or local configuration:
{
"kv_namespaces": [
{
"binding": "KV",
"id": "prod-id",
"preview_id": "preview-id" // Required for local dev
}
]
}D1 Migrations Not Applied
# Apply migrations to local database
bunx wrangler d1 migrations apply DB --localWrangler Configuration Reference
Complete guide to wrangler.jsonc configuration options.
Basic Configuration
{
// JSON Schema for IDE support
"$schema": "node_modules/wrangler/config-schema.json",
// Worker name (used in deployment)
"name": "my-worker",
// Entry point
"main": "src/index.ts",
// Compatibility date (controls runtime behavior)
"compatibility_date": "2024-12-01",
// Optional: Compatibility flags
"compatibility_flags": ["nodejs_compat"]
}Development Settings
{
"dev": {
// Local dev server port
"port": 8787,
// Local protocol (http or https)
"local_protocol": "http",
// Bind to IP (default: localhost)
"ip": "127.0.0.1"
}
}Environment Variables
Non-Secret Variables
{
"vars": {
"ENVIRONMENT": "production",
"LOG_LEVEL": "info",
"API_VERSION": "v1"
}
}Secrets (via CLI)
# Set secret
bunx wrangler secret put API_KEY
# Enter value when prompted
# List secrets
bunx wrangler secret list
# Delete secret
bunx wrangler secret delete API_KEYLocal Secrets (.dev.vars)
# .dev.vars (gitignored)
API_KEY=dev-key-123
DATABASE_URL=postgres://localhost/devBindings
KV Namespace
{
"kv_namespaces": [
{
"binding": "KV",
"id": "production-namespace-id",
"preview_id": "preview-namespace-id"
}
]
}D1 Database
{
"d1_databases": [
{
"binding": "DB",
"database_id": "database-uuid",
"database_name": "my-database",
"migrations_dir": "migrations" // Optional, default: "migrations"
}
]
}R2 Bucket
{
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-bucket",
"preview_bucket_name": "my-bucket-preview",
"jurisdiction": "eu" // Optional: data location
}
]
}Durable Objects
{
"durable_objects": {
"bindings": [
{
"name": "ROOMS",
"class_name": "ChatRoom",
"script_name": "chat-worker" // Optional: external worker
}
]
},
// Required for new DO classes
"migrations": [
{
"tag": "v1",
"new_classes": ["ChatRoom"]
}
]
}Queues
{
"queues": {
"producers": [
{
"binding": "MY_QUEUE",
"queue": "my-queue-name"
}
],
"consumers": [
{
"queue": "my-queue-name",
"max_batch_size": 10,
"max_batch_timeout": 5,
"max_retries": 3,
"dead_letter_queue": "my-dlq"
}
]
}
}Analytics Engine
{
"analytics_engine_datasets": [
{
"binding": "ANALYTICS",
"dataset": "my_worker_metrics"
}
]
}Service Bindings
{
"services": [
{
"binding": "AUTH_SERVICE",
"service": "auth-worker",
"environment": "production"
}
]
}Workers AI
{
"ai": {
"binding": "AI"
}
}Vectorize
{
"vectorize": [
{
"binding": "VECTORIZE",
"index_name": "my-index"
}
]
}Hyperdrive
{
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "hyperdrive-config-id"
}
]
}Routes and Triggers
HTTP Routes
{
"routes": [
{
"pattern": "example.com/*",
"zone_name": "example.com"
},
{
"pattern": "api.example.com/v1/*",
"zone_id": "zone-id-here"
}
]
}Custom Domains
{
"routes": [
{
"pattern": "api.example.com",
"custom_domain": true
}
]
}Cron Triggers
{
"triggers": {
"crons": [
"0 * * * *", // Every hour
"0 0 * * *", // Daily at midnight
"*/5 * * * *" // Every 5 minutes
]
}
}Build Configuration
{
// Custom build command
"build": {
"command": "bun run build",
"cwd": ".",
"watch_dir": "src"
},
// Rules for module types
"rules": [
{
"type": "Text",
"globs": ["**/*.txt", "**/*.html"]
},
{
"type": "Data",
"globs": ["**/*.bin"]
}
]
}Observability
{
"observability": {
"enabled": true,
"head_sampling_rate": 1 // 0-1, 1 = 100%
},
"tail_consumers": [
{
"service": "log-aggregator",
"environment": "production"
}
]
}Asset Handling
Static Assets
{
"assets": {
"directory": "./public",
"binding": "ASSETS"
}
}Site (Legacy)
{
"site": {
"bucket": "./public",
"entry-point": "workers-site"
}
}Environment Overrides
{
"name": "my-worker",
"vars": {
"ENVIRONMENT": "development"
},
"env": {
"staging": {
"name": "my-worker-staging",
"vars": {
"ENVIRONMENT": "staging"
},
"kv_namespaces": [
{
"binding": "KV",
"id": "staging-kv-id"
}
]
},
"production": {
"name": "my-worker-production",
"vars": {
"ENVIRONMENT": "production"
},
"kv_namespaces": [
{
"binding": "KV",
"id": "production-kv-id"
}
]
}
}
}Limits Configuration
{
"limits": {
"cpu_ms": 50 // Max CPU time per request
}
}Complete Example
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-api",
"main": "src/index.ts",
"compatibility_date": "2024-12-01",
"compatibility_flags": ["nodejs_compat"],
"dev": {
"port": 8787,
"local_protocol": "http"
},
"vars": {
"ENVIRONMENT": "development",
"LOG_LEVEL": "debug"
},
"kv_namespaces": [
{ "binding": "CACHE", "id": "cache-prod-id", "preview_id": "cache-dev-id" }
],
"d1_databases": [
{ "binding": "DB", "database_id": "db-uuid", "database_name": "mydb" }
],
"r2_buckets": [
{ "binding": "UPLOADS", "bucket_name": "uploads" }
],
"ai": { "binding": "AI" },
"observability": {
"enabled": true,
"head_sampling_rate": 1
},
"triggers": {
"crons": ["0 * * * *"]
},
"env": {
"production": {
"name": "my-api-production",
"vars": {
"ENVIRONMENT": "production",
"LOG_LEVEL": "warn"
}
}
}
}#!/bin/bash
# Development Environment Setup Script for Cloudflare Workers
#
# This script initializes a complete development environment including:
# - Project structure
# - TypeScript configuration
# - Wrangler configuration
# - Git setup
# - Development dependencies
#
# Usage:
# ./dev-setup.sh [project-name]
#
# Example:
# ./dev-setup.sh my-worker
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
# Check for required commands
check_requirements() {
info "Checking requirements..."
if ! command -v bun &> /dev/null; then
error "Bun is required. Install: curl -fsSL https://bun.sh/install | bash"
fi
if ! command -v git &> /dev/null; then
error "Git is required. Install: https://git-scm.com/downloads"
fi
success "All requirements met"
}
# Get project name
get_project_name() {
if [ -n "$1" ]; then
PROJECT_NAME="$1"
else
read -p "Enter project name: " PROJECT_NAME
fi
if [ -z "$PROJECT_NAME" ]; then
error "Project name is required"
fi
# Validate project name (lowercase, hyphens, alphanumeric)
if ! [[ "$PROJECT_NAME" =~ ^[a-z][a-z0-9-]*$ ]]; then
error "Project name must start with lowercase letter and contain only lowercase letters, numbers, and hyphens"
fi
info "Creating project: $PROJECT_NAME"
}
# Create project structure
create_project_structure() {
info "Creating project structure..."
mkdir -p "$PROJECT_NAME"
cd "$PROJECT_NAME"
# Create directories
mkdir -p src
mkdir -p test
mkdir -p migrations
mkdir -p public
success "Project structure created"
}
# Initialize package.json
init_package_json() {
info "Initializing package.json..."
cat > package.json << 'EOF'
{
"name": "PROJECT_NAME",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "wrangler dev",
"dev:remote": "wrangler dev --remote",
"dev:persist": "wrangler dev --persist",
"deploy": "wrangler deploy",
"deploy:staging": "wrangler deploy --env staging",
"deploy:production": "wrangler deploy --env production",
"test": "vitest",
"test:watch": "vitest --watch",
"test:coverage": "vitest --coverage",
"check": "tsc --noEmit",
"check:watch": "tsc --noEmit --watch",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"types": "wrangler types",
"tail": "wrangler tail",
"db:migrate": "wrangler d1 migrations apply DB",
"db:migrate:local": "wrangler d1 migrations apply DB --local",
"db:studio": "wrangler d1 execute DB --local --command 'SELECT 1'"
},
"devDependencies": {
"@cloudflare/vitest-pool-workers": "^0.7.0",
"@cloudflare/workers-types": "^4.20241230.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.0.0",
"typescript": "^5.7.0",
"vitest": "^2.1.0",
"wrangler": "^3.99.0"
},
"dependencies": {
"hono": "^4.6.0"
}
}
EOF
# Replace placeholder with actual project name
sed -i.bak "s/PROJECT_NAME/$PROJECT_NAME/g" package.json && rm package.json.bak
success "package.json created"
}
# Create TypeScript configuration
create_tsconfig() {
info "Creating TypeScript configuration..."
cat > tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"types": ["@cloudflare/workers-types"],
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
},
"include": ["src/**/*", "test/**/*"],
"exclude": ["node_modules", "dist"]
}
EOF
success "tsconfig.json created"
}
# Create Wrangler configuration
create_wrangler_config() {
info "Creating wrangler.jsonc..."
cat > wrangler.jsonc << EOF
{
"\$schema": "node_modules/wrangler/config-schema.json",
"name": "$PROJECT_NAME",
"main": "src/index.ts",
"compatibility_date": "2024-12-01",
"compatibility_flags": ["nodejs_compat"],
"dev": {
"port": 8787,
"local_protocol": "http"
},
"vars": {
"ENVIRONMENT": "development"
},
"observability": {
"enabled": true,
"head_sampling_rate": 1
},
// Environment overrides
"env": {
"staging": {
"name": "$PROJECT_NAME-staging",
"vars": {
"ENVIRONMENT": "staging"
}
},
"production": {
"name": "$PROJECT_NAME-production",
"vars": {
"ENVIRONMENT": "production"
}
}
}
}
EOF
success "wrangler.jsonc created"
}
# Create Vitest configuration
create_vitest_config() {
info "Creating Vitest configuration..."
cat > vitest.config.ts << 'EOF'
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
test: {
globals: true,
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' },
},
},
},
});
EOF
success "vitest.config.ts created"
}
# Create source files
create_source_files() {
info "Creating source files..."
# Main entry point
cat > src/index.ts << 'EOF'
import { Hono } from 'hono';
import { cors } from 'hono/cors';
interface Env {
ENVIRONMENT: string;
}
const app = new Hono<{ Bindings: Env }>();
// CORS middleware
app.use('*', cors());
// Health check
app.get('/health', (c) => {
return c.json({
status: 'healthy',
environment: c.env.ENVIRONMENT,
timestamp: new Date().toISOString(),
});
});
// API routes
app.get('/api', (c) => {
return c.json({
message: 'Hello from Cloudflare Workers!',
version: '1.0.0',
});
});
// 404 handler
app.notFound((c) => {
return c.json({ error: 'Not Found' }, 404);
});
// Error handler
app.onError((err, c) => {
console.error('Error:', err);
return c.json(
{
error: 'Internal Server Error',
message: c.env.ENVIRONMENT === 'development' ? err.message : undefined,
},
500
);
});
export default app;
EOF
success "Source files created"
}
# Create test files
create_test_files() {
info "Creating test files..."
cat > test/index.test.ts << 'EOF'
import { describe, it, expect } from 'vitest';
import { SELF } from 'cloudflare:test';
describe('Worker', () => {
it('returns healthy status', async () => {
const response = await SELF.fetch('http://localhost/health');
expect(response.status).toBe(200);
const data = await response.json();
expect(data.status).toBe('healthy');
});
it('returns API response', async () => {
const response = await SELF.fetch('http://localhost/api');
expect(response.status).toBe(200);
const data = await response.json();
expect(data.message).toBe('Hello from Cloudflare Workers!');
});
it('returns 404 for unknown routes', async () => {
const response = await SELF.fetch('http://localhost/unknown');
expect(response.status).toBe(404);
});
});
EOF
success "Test files created"
}
# Create Git configuration
create_git_config() {
info "Setting up Git..."
# .gitignore
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
# Wrangler
.wrangler/
.dev.vars
# TypeScript
*.tsbuildinfo
# Test
coverage/
# OS
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
*.swp
*.swo
# Build
dist/
*.log
EOF
# Initialize git repository
git init -q
git add .
git commit -q -m "Initial commit: Cloudflare Worker setup"
success "Git repository initialized"
}
# Create local secrets file
create_dev_vars() {
info "Creating .dev.vars template..."
cat > .dev.vars.example << 'EOF'
# Local development secrets
# Copy to .dev.vars and fill in values
# WARNING: Never commit .dev.vars to version control!
# API_KEY=your-api-key-here
# DATABASE_URL=postgres://localhost:5432/dev
EOF
success ".dev.vars.example created"
}
# Create VS Code settings
create_vscode_settings() {
info "Creating VS Code settings..."
mkdir -p .vscode
# VS Code settings
cat > .vscode/settings.json << 'EOF'
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.tsdk": "node_modules/typescript/lib"
}
EOF
# Launch configuration for debugging
cat > .vscode/launch.json << 'EOF'
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Worker",
"type": "node",
"request": "attach",
"port": 9229,
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/**"
]
}
]
}
EOF
success "VS Code settings created"
}
# Install dependencies
install_dependencies() {
info "Installing dependencies..."
bun install
success "Dependencies installed"
}
# Generate types
generate_types() {
info "Generating Cloudflare types..."
bunx wrangler types 2>/dev/null || warn "Could not generate types (run 'bun run types' after configuring bindings)"
}
# Main setup flow
main() {
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ Cloudflare Workers Development Environment Setup ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
check_requirements
get_project_name "$1"
if [ -d "$PROJECT_NAME" ]; then
error "Directory '$PROJECT_NAME' already exists"
fi
create_project_structure
init_package_json
create_tsconfig
create_wrangler_config
create_vitest_config
create_source_files
create_test_files
create_git_config
create_dev_vars
create_vscode_settings
install_dependencies
generate_types
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ Setup Complete! 🎉 ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "Next steps:"
echo ""
echo " 1. cd $PROJECT_NAME"
echo " 2. bun run dev # Start development server"
echo " 3. bun run test # Run tests"
echo " 4. bun run deploy # Deploy to Cloudflare"
echo ""
echo "Helpful commands:"
echo ""
echo " bun run dev:remote # Use real Cloudflare services"
echo " bun run tail # View real-time logs"
echo " bun run types # Regenerate types"
echo ""
echo "Documentation: https://developers.cloudflare.com/workers/"
echo ""
}
# Run main
main "$@"
/**
* Development Utilities for Cloudflare Workers
*
* Features:
* - Request/response inspection
* - Development-only middleware
* - Mock data generation
* - Performance measurement
* - Error simulation
*
* Usage:
* 1. Import needed utilities in development
* 2. Wrap handlers with dev middleware
* 3. Use inspectors for debugging
*/
// ============================================
// TYPES
// ============================================
interface Env {
ENVIRONMENT: string;
DEBUG?: string;
[key: string]: unknown;
}
interface TimingEntry {
name: string;
duration: number;
timestamp: number;
}
interface RequestLog {
id: string;
method: string;
url: string;
headers: Record<string, string>;
cf?: IncomingRequestCfProperties;
timestamp: number;
}
interface ResponseLog {
id: string;
status: number;
headers: Record<string, string>;
duration: number;
timestamp: number;
}
// ============================================
// ENVIRONMENT DETECTION
// ============================================
export function isDevelopment(env: Env): boolean {
return (
env.ENVIRONMENT === 'development' ||
env.DEBUG === 'true' ||
env.ENVIRONMENT === 'local'
);
}
export function isProduction(env: Env): boolean {
return env.ENVIRONMENT === 'production';
}
// ============================================
// REQUEST/RESPONSE INSPECTION
// ============================================
export function inspectRequest(request: Request): RequestLog {
return {
id: crypto.randomUUID(),
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers),
cf: request.cf,
timestamp: Date.now(),
};
}
export function inspectResponse(
response: Response,
requestId: string,
startTime: number
): ResponseLog {
return {
id: requestId,
status: response.status,
headers: Object.fromEntries(response.headers),
duration: Date.now() - startTime,
timestamp: Date.now(),
};
}
// ============================================
// DEVELOPMENT LOGGER
// ============================================
export class DevLogger {
private enabled: boolean;
private prefix: string;
constructor(env: Env, prefix = '[DEV]') {
this.enabled = isDevelopment(env);
this.prefix = prefix;
}
log(...args: unknown[]): void {
if (this.enabled) {
console.log(this.prefix, ...args);
}
}
debug(...args: unknown[]): void {
if (this.enabled) {
console.log(`${this.prefix} [DEBUG]`, ...args);
}
}
info(...args: unknown[]): void {
if (this.enabled) {
console.log(`${this.prefix} [INFO]`, ...args);
}
}
warn(...args: unknown[]): void {
if (this.enabled) {
console.warn(`${this.prefix} [WARN]`, ...args);
}
}
error(...args: unknown[]): void {
// Always log errors
console.error(`${this.prefix} [ERROR]`, ...args);
}
request(req: Request): void {
if (this.enabled) {
const info = inspectRequest(req);
console.log(`${this.prefix} Request:`, JSON.stringify(info, null, 2));
}
}
response(res: Response, requestId: string, startTime: number): void {
if (this.enabled) {
const info = inspectResponse(res, requestId, startTime);
console.log(`${this.prefix} Response:`, JSON.stringify(info, null, 2));
}
}
}
// ============================================
// PERFORMANCE TIMING
// ============================================
export class PerformanceTimer {
private entries: TimingEntry[] = [];
private marks: Map<string, number> = new Map();
mark(name: string): void {
this.marks.set(name, Date.now());
}
measure(name: string, startMark: string): number {
const start = this.marks.get(startMark);
if (!start) {
throw new Error(`Mark "${startMark}" not found`);
}
const duration = Date.now() - start;
this.entries.push({
name,
duration,
timestamp: Date.now(),
});
return duration;
}
async time<T>(name: string, fn: () => Promise<T>): Promise<T> {
const start = Date.now();
try {
return await fn();
} finally {
this.entries.push({
name,
duration: Date.now() - start,
timestamp: Date.now(),
});
}
}
getEntries(): TimingEntry[] {
return [...this.entries];
}
getSummary(): Record<string, number> {
const summary: Record<string, number> = {};
for (const entry of this.entries) {
summary[entry.name] = entry.duration;
}
return summary;
}
toHeader(): string {
return this.entries
.map((e) => `${e.name};dur=${e.duration}`)
.join(', ');
}
clear(): void {
this.entries = [];
this.marks.clear();
}
}
// ============================================
// DEVELOPMENT MIDDLEWARE
// ============================================
type Handler = (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
export function withDevMiddleware(handler: Handler): Handler {
return async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
if (!isDevelopment(env)) {
return handler(request, env, ctx);
}
const requestId = crypto.randomUUID();
const startTime = Date.now();
const timer = new PerformanceTimer();
const logger = new DevLogger(env);
timer.mark('start');
logger.request(request);
try {
const response = await timer.time('handler', () =>
handler(request, env, ctx)
);
// Add debug headers
const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Request-Id', requestId);
newResponse.headers.set('X-Response-Time', `${Date.now() - startTime}ms`);
newResponse.headers.set('Server-Timing', timer.toHeader());
logger.response(newResponse, requestId, startTime);
return newResponse;
} catch (error) {
logger.error('Handler error:', error);
throw error;
}
};
}
// ============================================
// CORS MIDDLEWARE (Development)
// ============================================
export function withDevCors(handler: Handler): Handler {
return async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
},
});
}
const response = await handler(request, env, ctx);
const newResponse = new Response(response.body, response);
newResponse.headers.set('Access-Control-Allow-Origin', '*');
return newResponse;
};
}
// ============================================
// MOCK DATA GENERATION
// ============================================
export const MockData = {
user(): { id: string; name: string; email: string; createdAt: string } {
const id = crypto.randomUUID();
return {
id,
name: `User ${id.slice(0, 4)}`,
email: `user-${id.slice(0, 8)}@example.com`,
createdAt: new Date().toISOString(),
};
},
users(count: number): Array<ReturnType<typeof MockData.user>> {
return Array.from({ length: count }, () => this.user());
},
post(): { id: string; title: string; content: string; authorId: string } {
const id = crypto.randomUUID();
return {
id,
title: `Post ${id.slice(0, 4)}`,
content: `This is the content of post ${id.slice(0, 8)}`,
authorId: crypto.randomUUID(),
};
},
randomString(length: number): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from(
{ length },
() => chars[Math.floor(Math.random() * chars.length)]
).join('');
},
randomNumber(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
randomBoolean(): boolean {
return Math.random() > 0.5;
},
randomDate(start: Date, end: Date): Date {
return new Date(
start.getTime() + Math.random() * (end.getTime() - start.getTime())
);
},
};
// ============================================
// ERROR SIMULATION (Testing)
// ============================================
export function withErrorSimulation(handler: Handler, errorRate = 0.1): Handler {
return async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
if (!isDevelopment(env)) {
return handler(request, env, ctx);
}
// Simulate random errors
if (Math.random() < errorRate) {
const errors = [
{ status: 500, message: 'Simulated server error' },
{ status: 503, message: 'Simulated service unavailable' },
{ status: 429, message: 'Simulated rate limit' },
{ status: 408, message: 'Simulated timeout' },
];
const error = errors[Math.floor(Math.random() * errors.length)];
return new Response(JSON.stringify({ error: error.message }), {
status: error.status,
headers: { 'Content-Type': 'application/json' },
});
}
return handler(request, env, ctx);
};
}
// ============================================
// LATENCY SIMULATION (Testing)
// ============================================
export function withLatencySimulation(
handler: Handler,
minMs = 100,
maxMs = 500
): Handler {
return async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
if (!isDevelopment(env)) {
return handler(request, env, ctx);
}
const delay = MockData.randomNumber(minMs, maxMs);
await new Promise((resolve) => setTimeout(resolve, delay));
return handler(request, env, ctx);
};
}
// ============================================
// REQUEST BODY INSPECTION
// ============================================
export async function inspectBody(request: Request): Promise<{
text?: string;
json?: unknown;
formData?: Record<string, string>;
size: number;
}> {
const clone = request.clone();
const contentType = request.headers.get('Content-Type') || '';
if (contentType.includes('application/json')) {
try {
const text = await clone.text();
return {
text,
json: JSON.parse(text),
size: text.length,
};
} catch {
return { size: 0 };
}
}
if (contentType.includes('form')) {
try {
const formData = await clone.formData();
const data: Record<string, string> = {};
formData.forEach((value, key) => {
data[key] = value.toString();
});
return { formData: data, size: JSON.stringify(data).length };
} catch {
return { size: 0 };
}
}
const text = await clone.text();
return { text, size: text.length };
}
// ============================================
// DEBUG ENDPOINTS
// ============================================
export function createDebugEndpoints(env: Env): Record<string, () => Response> {
if (!isDevelopment(env)) {
return {};
}
return {
'/__debug/env': () =>
Response.json({
environment: env.ENVIRONMENT,
debug: env.DEBUG,
bindings: Object.keys(env).filter(
(k) => !['ENVIRONMENT', 'DEBUG'].includes(k)
),
}),
'/__debug/headers': () =>
Response.json({
note: 'Send a request to see headers',
example: 'curl -H "X-Test: value" /__debug/echo',
}),
'/__debug/time': () =>
Response.json({
timestamp: Date.now(),
iso: new Date().toISOString(),
timezone: 'UTC',
}),
'/__debug/random': () =>
Response.json({
uuid: crypto.randomUUID(),
string: MockData.randomString(32),
number: MockData.randomNumber(1, 1000),
boolean: MockData.randomBoolean(),
}),
};
}
// ============================================
// EXAMPLE USAGE
// ============================================
/*
import {
withDevMiddleware,
withDevCors,
DevLogger,
PerformanceTimer,
MockData,
createDebugEndpoints,
} from './dev-script';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Debug endpoints (development only)
const debugEndpoints = createDebugEndpoints(env);
const url = new URL(request.url);
const debugHandler = debugEndpoints[url.pathname];
if (debugHandler) {
return debugHandler();
}
// Your main handler
return handleRequest(request, env, ctx);
},
};
// Wrap with middleware
const handler = withDevCors(withDevMiddleware(handleRequest));
*/
{
// JSON Schema for IDE support (autocompletion, validation)
"$schema": "node_modules/wrangler/config-schema.json",
// ============================================
// BASIC CONFIGURATION
// ============================================
// Worker name (used in deployment URL: <name>.<subdomain>.workers.dev)
"name": "my-worker",
// Entry point (TypeScript supported out of box)
"main": "src/index.ts",
// Controls runtime behavior - update quarterly
// See: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
"compatibility_date": "2024-12-01",
// Enable Node.js compatibility (Buffer, crypto, etc.)
"compatibility_flags": ["nodejs_compat"],
// ============================================
// DEVELOPMENT SETTINGS
// ============================================
"dev": {
// Local dev server port
"port": 8787,
// Protocol (http for local, https if testing TLS)
"local_protocol": "http",
// Bind to IP (localhost by default, 0.0.0.0 for network access)
"ip": "127.0.0.1"
},
// ============================================
// ENVIRONMENT VARIABLES (Non-Secret)
// ============================================
// These are NOT secrets - visible in dashboard and logs
// Use `wrangler secret put SECRET_NAME` for secrets
"vars": {
"ENVIRONMENT": "development",
"LOG_LEVEL": "debug",
"API_VERSION": "v1"
},
// ============================================
// KV NAMESPACES
// ============================================
// Key-value storage for caching, session data, etc.
// Create: bunx wrangler kv:namespace create "CACHE"
"kv_namespaces": [
{
"binding": "CACHE", // Access as env.CACHE
"id": "your-kv-id", // Production namespace ID
"preview_id": "your-preview-kv-id" // Local dev namespace ID
}
],
// ============================================
// D1 DATABASES
// ============================================
// Serverless SQLite database
// Create: bunx wrangler d1 create my-database
"d1_databases": [
{
"binding": "DB", // Access as env.DB
"database_id": "your-database-id",
"database_name": "my-database",
"migrations_dir": "migrations" // Default: migrations/
}
],
// ============================================
// R2 BUCKETS
// ============================================
// Object storage (S3-compatible)
// Create: bunx wrangler r2 bucket create my-bucket
"r2_buckets": [
{
"binding": "UPLOADS", // Access as env.UPLOADS
"bucket_name": "my-bucket",
"preview_bucket_name": "my-bucket-preview", // For local dev
"jurisdiction": "eu" // Optional: data residency
}
],
// ============================================
// DURABLE OBJECTS
// ============================================
// Stateful objects for real-time coordination
"durable_objects": {
"bindings": [
{
"name": "ROOMS", // Access as env.ROOMS
"class_name": "ChatRoom" // Export class from your code
}
]
},
// Required for new Durable Object classes
"migrations": [
{
"tag": "v1",
"new_classes": ["ChatRoom"]
}
],
// ============================================
// QUEUES
// ============================================
// Message queues for async processing
// Create: bunx wrangler queues create my-queue
"queues": {
"producers": [
{
"binding": "TASKS", // Access as env.TASKS
"queue": "task-queue"
}
],
"consumers": [
{
"queue": "task-queue",
"max_batch_size": 10, // Messages per batch (1-100)
"max_batch_timeout": 5, // Seconds to wait for batch
"max_retries": 3, // Retry failed messages
"dead_letter_queue": "task-dlq" // Failed messages destination
}
]
},
// ============================================
// WORKERS AI
// ============================================
// Access AI models
"ai": {
"binding": "AI" // Access as env.AI
},
// ============================================
// VECTORIZE
// ============================================
// Vector database for embeddings
// Create: bunx wrangler vectorize create my-index --dimensions=1536 --metric=cosine
"vectorize": [
{
"binding": "VECTORS", // Access as env.VECTORS
"index_name": "my-index"
}
],
// ============================================
// HYPERDRIVE
// ============================================
// Connection pooling for PostgreSQL
// Create: bunx wrangler hyperdrive create my-config --connection-string="..."
"hyperdrive": [
{
"binding": "HYPERDRIVE", // Access as env.HYPERDRIVE
"id": "your-hyperdrive-config-id"
}
],
// ============================================
// SERVICE BINDINGS
// ============================================
// Call other workers directly (no HTTP overhead)
"services": [
{
"binding": "AUTH", // Access as env.AUTH
"service": "auth-worker", // Target worker name
"environment": "production" // Target environment
}
],
// ============================================
// ANALYTICS ENGINE
// ============================================
// Custom metrics and analytics
"analytics_engine_datasets": [
{
"binding": "ANALYTICS", // Access as env.ANALYTICS
"dataset": "worker_metrics"
}
],
// ============================================
// STATIC ASSETS
// ============================================
// Serve static files (HTML, CSS, JS, images)
"assets": {
"directory": "./public", // Static files directory
"binding": "ASSETS" // Optional: access programmatically
},
// ============================================
// ROUTES AND TRIGGERS
// ============================================
// HTTP routes (requires zone ownership)
"routes": [
{
"pattern": "api.example.com/*",
"zone_name": "example.com"
},
{
"pattern": "example.com/api/*",
"zone_id": "your-zone-id"
}
],
// Cron triggers for scheduled tasks
"triggers": {
"crons": [
"0 * * * *", // Every hour
"0 0 * * *", // Daily at midnight UTC
"*/5 * * * *" // Every 5 minutes
]
},
// ============================================
// OBSERVABILITY
// ============================================
"observability": {
"enabled": true,
"head_sampling_rate": 1 // 0-1 (1 = 100% of requests)
},
// Send logs to another worker
"tail_consumers": [
{
"service": "log-aggregator",
"environment": "production"
}
],
// ============================================
// BUILD CONFIGURATION
// ============================================
// Custom build command (optional)
"build": {
"command": "bun run build",
"cwd": ".",
"watch_dir": "src"
},
// Module type rules
"rules": [
{
"type": "Text",
"globs": ["**/*.txt", "**/*.html", "**/*.sql"]
},
{
"type": "Data",
"globs": ["**/*.bin", "**/*.wasm"]
}
],
// ============================================
// LIMITS
// ============================================
"limits": {
"cpu_ms": 50 // Max CPU time per request
},
// ============================================
// ENVIRONMENT OVERRIDES
// ============================================
// Override settings per environment
// Deploy: bunx wrangler deploy --env production
"env": {
"staging": {
"name": "my-worker-staging",
"vars": {
"ENVIRONMENT": "staging",
"LOG_LEVEL": "info"
},
"kv_namespaces": [
{
"binding": "CACHE",
"id": "staging-kv-id"
}
]
},
"production": {
"name": "my-worker-production",
"vars": {
"ENVIRONMENT": "production",
"LOG_LEVEL": "warn"
},
"kv_namespaces": [
{
"binding": "CACHE",
"id": "production-kv-id"
}
],
"routes": [
{
"pattern": "api.example.com/*",
"zone_name": "example.com"
}
]
}
}
}