Cloudflare Workers Reference Sheet

Just a guy who loves to write code and watch anime.
Core Concepts
Workers Runtime: V8 isolates (not Node.js) running on Cloudflare's edge network
Cold start: <1ms vs 100ms+ for containers
Web APIs only (no
fs,process.env)Each request is isolated and stateless
Basic Structure:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Your logic here
return new Response('Hello World');
}
}
Request Handling
Request Object:
const url = new URL(request.url);
const method = request.method; // GET, POST, etc.
const headers = request.headers;
const body = await request.json(); // or .text()
// Query parameters
const userId = url.searchParams.get('userId'); // ?userId=123
Response Object:
// Simple text
return new Response('Hello');
// JSON with headers
return new Response(JSON.stringify({data: 'value'}), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
// Error response
return new Response(JSON.stringify({error: 'Bad request'}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
Basic Routing
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method;
if (path === '/api/users' && method === 'GET') {
return new Response(JSON.stringify([{id: 1, name: 'Alice'}]), {
headers: { 'Content-Type': 'application/json' }
});
}
if (path === '/api/users' && method === 'POST') {
const userData = await request.json();
// Validation
if (!userData.name) {
return new Response(JSON.stringify({error: 'Name required'}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
// Process...
return new Response(JSON.stringify({id: 123, ...userData}), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response('Not Found', { status: 404 });
}
}
Environment & Configuration
wrangler.toml:
name = "my-worker"
compatibility_date = "2024-01-01"
# Non-secret environment variables
[vars]
API_URL = "https://api.example.com"
DEBUG = "true"
Secrets (sensitive data):
# Set secrets via CLI
wrangler secret put API_KEY
wrangler secret put DATABASE_PASSWORD
Using in code:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const apiKey = env.API_KEY; // secret
const apiUrl = env.API_URL; // regular env var
return new Response(`Using ${apiUrl}`);
}
}
TypeScript setup:
# Generate types for your specific Worker config
pnpm wrangler types
Add to tsconfig.json:
{
"compilerOptions": {
"types": ["./worker-configuration.d.ts"]
}
}
External API Calls
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
try {
// Call external API
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: 'example' })
});
if (!response.ok) {
return new Response(JSON.stringify({error: 'External API failed'}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
const data = await response.json();
// Transform and return
return new Response(JSON.stringify({
result: data.someField,
timestamp: Date.now()
}), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({error: 'Request failed'}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
}
Caching
Cache API (like a persistent Map<string, Response>):
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default; // or await caches.open('my-cache')
const cacheKey = 'weather-london';
// Try cache first
const cachedResponse = await cache.match(cacheKey);
if (cachedResponse) {
return cachedResponse;
}
// Cache miss - fetch fresh data
const freshResponse = await fetch(`https://api.weather.com?key=${env.API_KEY}`);
const data = await freshResponse.json();
const response = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=300' // Browser caches for 5 min
}
});
// Store in Cloudflare cache (background)
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
}
Key concepts:
caches.default= global cache,caches.open('name')= named cacheresponse.clone()= needed because Response can only be read oncectx.waitUntil()= keep Worker alive for background task, don't block responseCache-Controlheader = browser caching, separate fromcachesAPI
Middleware Pattern
// Middleware functions return Response to stop, null to continue
async function corsMiddleware(request: Request): Promise<Response | null> {
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
}
return null;
}
async function authMiddleware(request: Request): Promise<Response | null> {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({error: 'Unauthorized'}), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
return null;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Run middleware chain
const middlewares = [corsMiddleware];
for (const middleware of middlewares) {
const response = await middleware(request);
if (response) return response;
}
// Protected routes
const url = new URL(request.url);
if (url.pathname.startsWith('/api/protected/')) {
const authResponse = await authMiddleware(request);
if (authResponse) return authResponse;
}
// Your routes...
return new Response('OK');
}
}
Streaming Responses
For large datasets or real-time data:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 10000; i++) {
// Each line is a separate JSON object
const chunk = JSON.stringify({id: i, data: `Item ${i}`}) + '\n';
controller.enqueue(new TextEncoder().encode(chunk));
// Yield occasionally
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
controller.close();
}
});
return new Response(stream, {
headers: {
'Content-Type': 'application/x-ndjson', // Newline-delimited JSON
'Transfer-Encoding': 'chunked'
}
});
}
}
Production Patterns
Error handling & logging:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const startTime = Date.now();
try {
// Your logic...
const response = new Response(JSON.stringify({success: true}), {
headers: { 'Content-Type': 'application/json' }
});
// Success metrics
console.log(`Success: ${request.method} ${new URL(request.url).pathname} - ${Date.now() - startTime}ms`);
return response;
} catch (error) {
console.error(`Error: ${request.method} ${request.url}`, error);
return new Response(JSON.stringify({error: 'Internal Server Error'}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
}
Simple rate limiting:
const rateLimits = new Map<string, {count: number; resetTime: number}>();
function isRateLimited(ip: string, limit: number, windowMs: number): boolean {
const now = Date.now();
const current = rateLimits.get(ip);
// No data or window expired - reset
if (!current || now > current.resetTime) {
rateLimits.set(ip, {count: 1, resetTime: now + windowMs});
return false;
}
// Hit limit
if (current.count >= limit) {
return true;
}
// Increment and allow
current.count++;
return false;
}
Key Reminders
Stateless: Variables don't persist between requests
Web APIs: Use
fetch,Request,Response,URL- not Node.js APIsSecrets vs Env Vars: Secrets via CLI, regular config in wrangler.toml
TypeScript: Run
wrangler typesafter config changesCaching:
cachesAPI for persistence,ctx.waitUntil()for background tasksRate Limiting: Implement your own, resets on each deployment (use KV for persistence)
Deployment
# Deploy to production
wrangler deploy
# Deploy to staging
wrangler deploy --env staging
# View logs
wrangler tail






