Cloudflare Developer Platform Skill
Cloudflare Developer Platform is a comprehensive edge computing ecosystem for building full-stack applications on Cloudflare's global network. It includes serverless functions, databases, storage, AI/ML capabilities, and static site hosting.
When to Use This Skill
Use this skill when:
- •Building serverless applications on the edge
- •Implementing edge databases (D1 SQLite)
- •Working with object storage (R2) or key-value stores (KV)
- •Creating real-time applications with WebSockets (Durable Objects)
- •Integrating AI/ML capabilities (Workers AI, AI Gateway, Agents)
- •Deploying static sites with serverless functions (Pages)
- •Building full-stack applications with frameworks (Next.js, Remix, Astro, etc.)
- •Implementing message queues and background jobs (Queues)
- •Optimizing for global performance and low latency
Core Concepts
Edge Computing Platform
Cloudflare's Edge Network: Code runs on servers globally distributed across 300+ cities, executing requests from the nearest location for ultra-low latency.
Key Components:
- •Workers: Serverless functions on the edge
- •D1: SQLite database with global read replication
- •KV: Distributed key-value store with eventual consistency
- •R2: Object storage with zero egress fees
- •Durable Objects: Stateful compute with WebSocket support
- •Queues: Message queue system for async processing
- •Pages: Static site hosting with serverless functions
- •Workers AI: Run AI models on the edge
- •AI Gateway: Unified interface for AI providers
Execution Model
V8 Isolates: Lightweight execution environments (faster than containers) with:
- •Millisecond cold starts
- •Zero infrastructure management
- •Automatic scaling
- •Pay-per-request pricing
Handler Types:
- •
fetch: HTTP requests - •
scheduled: Cron jobs - •
queue: Message processing - •
tail: Log aggregation - •
email: Email handling - •
alarm: Durable Object timers
Getting Started with Workers
Installation
# Install Wrangler CLI npm install -g wrangler # Login to Cloudflare wrangler login # Create new project wrangler init my-worker cd my-worker # Start local development wrangler dev # Deploy to production wrangler deploy
Basic Worker
// src/index.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return new Response('Hello from Cloudflare Workers!');
},
};
Configuration (wrangler.toml)
name = "my-worker" main = "src/index.ts" compatibility_date = "2024-01-01" # Environment variables [vars] ENVIRONMENT = "production" # Bindings (added per product below)
Language Support
- •JavaScript/TypeScript: Primary language (full Node.js compatibility)
- •Python: Beta support via Workers Python
- •Rust: Compile to WebAssembly
Storage Products
D1 (SQLite Database)
Use Cases: Relational data, complex queries, ACID transactions
Setup:
# Create database wrangler d1 create my-database # Add to wrangler.toml [[d1_databases]] binding = "DB" database_name = "my-database" database_id = "YOUR_DATABASE_ID" # Generate and apply schema wrangler d1 execute my-database --file=./schema.sql
Usage:
export default {
async fetch(request: Request, env: Env) {
// Query
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
// Insert
await env.DB.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
.bind('Alice', 'alice@example.com')
.run();
// Batch (atomic)
await env.DB.batch([
env.DB.prepare('UPDATE accounts SET balance = balance - 100 WHERE id = ?').bind(user1),
env.DB.prepare('UPDATE accounts SET balance = balance + 100 WHERE id = ?').bind(user2),
]);
return new Response(JSON.stringify(result));
},
};
Key Features:
- •Global read replication (low-latency reads)
- •Single-writer consistency
- •Standard SQLite syntax
- •25GB database size limit
KV (Key-Value Store)
Use Cases: Cache, sessions, feature flags, rate limiting
Setup:
# Create namespace wrangler kv:namespace create MY_KV # Add to wrangler.toml [[kv_namespaces]] binding = "KV" id = "YOUR_NAMESPACE_ID"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Put with TTL
await env.KV.put('session:token', JSON.stringify(data), {
expirationTtl: 3600, // 1 hour
});
// Get
const data = await env.KV.get('session:token', 'json');
// Delete
await env.KV.delete('session:token');
// List with prefix
const list = await env.KV.list({ prefix: 'user:123:' });
return new Response(JSON.stringify(data));
},
};
Key Features:
- •Sub-millisecond reads (edge-cached)
- •Eventual consistency (~60 seconds globally)
- •25MB value size limit
- •Automatic expiration (TTL)
R2 (Object Storage)
Use Cases: File storage, media hosting, backups, static assets
Setup:
# Create bucket wrangler r2 bucket create my-bucket # Add to wrangler.toml [[r2_buckets]] binding = "R2_BUCKET" bucket_name = "my-bucket"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Put object
await env.R2_BUCKET.put('path/to/file.jpg', fileBuffer, {
httpMetadata: {
contentType: 'image/jpeg',
},
});
// Get object
const object = await env.R2_BUCKET.get('path/to/file.jpg');
if (!object) {
return new Response('Not found', { status: 404 });
}
// Stream response
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
},
});
// Delete
await env.R2_BUCKET.delete('path/to/file.jpg');
// List
const list = await env.R2_BUCKET.list({ prefix: 'uploads/' });
},
};
Key Features:
- •S3-compatible API
- •Zero egress fees (huge cost advantage)
- •Unlimited storage
- •5TB object size limit
- •Multipart upload support
Durable Objects
Use Cases: Real-time apps, WebSockets, coordination, stateful logic
Setup:
# wrangler.toml [[durable_objects.bindings]] name = "COUNTER" class_name = "Counter" script_name = "my-worker"
Usage:
// Define Durable Object class
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
}
async fetch(request: Request) {
// Get current count
let count = (await this.state.storage.get<number>('count')) || 0;
// Increment
count++;
await this.state.storage.put('count', count);
return new Response(JSON.stringify({ count }));
}
}
// Use in Worker
export default {
async fetch(request: Request, env: Env) {
// Get Durable Object instance
const id = env.COUNTER.idFromName('global-counter');
const counter = env.COUNTER.get(id);
// Forward request
return counter.fetch(request);
},
};
WebSocket Example:
export class ChatRoom {
state: DurableObjectState;
sessions: Set<WebSocket>;
constructor(state: DurableObjectState) {
this.state = state;
this.sessions = new Set();
}
async fetch(request: Request) {
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
this.state.acceptWebSocket(server);
this.sessions.add(server);
return new Response(null, { status: 101, webSocket: client });
}
async webSocketMessage(ws: WebSocket, message: string) {
// Broadcast to all connected clients
for (const session of this.sessions) {
session.send(message);
}
}
async webSocketClose(ws: WebSocket) {
this.sessions.delete(ws);
}
}
Key Features:
- •Single-instance coordination (strong consistency)
- •Persistent storage (1GB limit on paid plans)
- •WebSocket support
- •Automatic hibernation for inactive objects
Queues
Use Cases: Background jobs, email sending, async processing
Setup:
# wrangler.toml [[queues.producers]] binding = "MY_QUEUE" queue = "my-queue" [[queues.consumers]] queue = "my-queue" max_batch_size = 10 max_batch_timeout = 30
Usage:
// Producer: Send messages
export default {
async fetch(request: Request, env: Env) {
await env.MY_QUEUE.send({
type: 'email',
to: 'user@example.com',
subject: 'Welcome!',
});
return new Response('Message queued');
},
};
// Consumer: Process messages
export default {
async queue(batch: MessageBatch<any>, env: Env) {
for (const message of batch.messages) {
try {
await processMessage(message.body);
message.ack(); // Acknowledge success
} catch (error) {
message.retry(); // Retry on failure
}
}
},
};
Key Features:
- •At-least-once delivery
- •Automatic retries (exponential backoff)
- •Dead-letter queue support
- •Batch processing
AI Products
Workers AI
Use Cases: Run AI models directly on the edge
Setup:
# wrangler.toml [ai] binding = "AI"
Usage:
export default {
async fetch(request: Request, env: Env) {
// Text generation
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [{ role: 'user', content: 'What is edge computing?' }],
});
// Image classification
const imageResponse = await env.AI.run('@cf/microsoft/resnet-50', {
image: imageBuffer,
});
// Text embeddings
const embeddings = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: 'Hello world',
});
return new Response(JSON.stringify(response));
},
};
Available Models:
- •LLMs: Llama 3, Mistral, Gemma, Qwen
- •Image: Stable Diffusion, DALL-E, ResNet
- •Embeddings: BGE, GTE
- •Translation, summarization, sentiment analysis
AI Gateway
Use Cases: Unified interface for AI providers with caching, rate limiting, analytics
Setup:
// OpenAI via AI Gateway
const response = await fetch(
'https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai/chat/completions',
{
method: 'POST',
headers: {
Authorization: `Bearer ${env.OPENAI_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }],
}),
}
);
Features:
- •Request caching (reduce costs)
- •Rate limiting
- •Analytics and logging
- •Supports OpenAI, Anthropic, HuggingFace, etc.
Agents
Use Cases: Build AI agents with tools and workflows
import { Agent } from '@cloudflare/agents';
export default {
async fetch(request: Request, env: Env) {
const agent = new Agent({
model: '@cf/meta/llama-3-8b-instruct',
tools: [
{
name: 'get_weather',
description: 'Get current weather',
parameters: {
type: 'object',
properties: {
location: { type: 'string' },
},
},
handler: async ({ location }) => {
// Fetch weather data
return { temperature: 72, conditions: 'sunny' };
},
},
],
});
const result = await agent.run('What is the weather in San Francisco?');
return new Response(JSON.stringify(result));
},
};
AI Search (RAG)
Use Cases: Build retrieval-augmented generation applications
import { VectorizeIndex } from '@cloudflare/workers-types';
export default {
async fetch(request: Request, env: Env) {
// Generate embeddings
const embeddings = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: query,
});
// Search vector database
const results = await env.VECTORIZE_INDEX.query(embeddings.data[0], {
topK: 5,
});
// Generate response with context
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [
{
role: 'system',
content: `Context: ${results.matches.map((m) => m.metadata.text).join('\n')}`,
},
{ role: 'user', content: query },
],
});
return new Response(JSON.stringify(response));
},
};
Cloudflare Pages
Static Sites + Serverless Functions
Deployment:
# Deploy via Git (recommended) # Connect GitHub repo in Cloudflare dashboard # Or deploy via CLI wrangler pages deploy ./dist
Pages Functions
Directory-based routing in functions/:
functions/ ├── api/ │ ├── users/ │ │ └── [id].ts # /api/users/:id │ └── posts.ts # /api/posts └── _middleware.ts # Global middleware
Example Function:
// functions/api/users/[id].ts
export async function onRequestGet(context) {
const { params, env } = context;
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(params.id).first();
return new Response(JSON.stringify(user), {
headers: { 'Content-Type': 'application/json' },
});
}
Middleware:
// functions/_middleware.ts
export async function onRequest(context) {
const start = Date.now();
const response = await context.next();
const duration = Date.now() - start;
console.log(`${context.request.method} ${context.request.url} - ${duration}ms`);
return response;
}
Framework Support
Next.js:
npx create-next-app@latest my-app cd my-app npm install -D @cloudflare/next-on-pages npx @cloudflare/next-on-pages wrangler pages deploy .vercel/output/static
Remix:
npx create-remix@latest --template cloudflare/remix
Astro:
npm create astro@latest # Select "Cloudflare" adapter during setup
SvelteKit:
npm create svelte@latest npm install -D @sveltejs/adapter-cloudflare
Wrangler CLI Essentials
Core Commands
# Development wrangler dev # Local development server wrangler dev --remote # Dev on real Cloudflare infrastructure # Deployment wrangler deploy # Deploy to production wrangler deploy --dry-run # Preview changes without deploying # Logs wrangler tail # Real-time logs wrangler tail --format pretty # Formatted logs # Versions wrangler deployments list # List deployments wrangler rollback [version] # Rollback to previous version # Secrets wrangler secret put SECRET_NAME # Add secret wrangler secret list # List secrets wrangler secret delete SECRET_NAME # Delete secret
Project Management
# Create projects wrangler init my-worker # Create Worker wrangler pages project create # Create Pages project # Database wrangler d1 create my-db # Create D1 database wrangler d1 execute my-db --file=schema.sql wrangler d1 execute my-db --command="SELECT * FROM users" # KV wrangler kv:namespace create MY_KV wrangler kv:key put --binding=MY_KV "key" "value" wrangler kv:key get --binding=MY_KV "key" # R2 wrangler r2 bucket create my-bucket wrangler r2 object put my-bucket/file.txt --file=./file.txt
Integration Patterns
Full-Stack Application Architecture
┌─────────────────────────────────────────┐
│ Cloudflare Pages (Frontend) │
│ Next.js / Remix / Astro / SvelteKit │
└──────────────────┬──────────────────────┘
│
┌──────────────────▼──────────────────────┐
│ Workers (API Layer / BFF) │
│ - Routing │
│ - Authentication │
│ - Business logic │
└─┬──────┬──────┬──────┬──────┬───────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────────────┐
│ D1 │ │ KV │ │ R2 │ │ DO │ │ Workers AI │
└────┘ └────┘ └────┘ └────┘ └────────────┘
Polyglot Storage Pattern
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// KV: Fast cache
const cached = await env.KV.get(url.pathname);
if (cached) return new Response(cached);
// D1: Structured data
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
// R2: Media files
const avatar = await env.R2_BUCKET.get(`avatars/${user.id}.jpg`);
// Durable Objects: Real-time coordination
const chat = env.CHAT_ROOM.get(env.CHAT_ROOM.idFromName(roomId));
// Queue: Async processing
await env.EMAIL_QUEUE.send({ to: user.email, template: 'welcome' });
return new Response(JSON.stringify({ user, avatar }));
},
};
Authentication Pattern
import { verifyJWT, createJWT } from './jwt';
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
// Login
if (url.pathname === '/api/login') {
const { email, password } = await request.json();
const user = await env.DB.prepare('SELECT * FROM users WHERE email = ?').bind(email).first();
if (!user || !(await verifyPassword(password, user.password_hash))) {
return new Response('Invalid credentials', { status: 401 });
}
const token = await createJWT({ userId: user.id }, env.JWT_SECRET);
return new Response(JSON.stringify({ token }), {
headers: { 'Content-Type': 'application/json' },
});
}
// Protected route
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.replace('Bearer ', '');
const payload = await verifyJWT(token, env.JWT_SECRET);
// Store session in KV
await env.KV.put(`session:${payload.userId}`, JSON.stringify(payload), {
expirationTtl: 86400, // 24 hours
});
return new Response('Authenticated');
},
};
Cache Strategy
export default {
async fetch(request: Request, env: Env) {
const cache = caches.default;
const cacheKey = new Request(request.url);
// Check cache
let response = await cache.match(cacheKey);
if (response) return response;
// Check KV (distributed cache)
const kvCached = await env.KV.get(request.url);
if (kvCached) {
response = new Response(kvCached);
await cache.put(cacheKey, response.clone());
return response;
}
// Fetch from origin (D1, R2, etc.)
const data = await fetchFromOrigin(request, env);
response = new Response(data);
// Store in both caches
await cache.put(cacheKey, response.clone());
await env.KV.put(request.url, data, { expirationTtl: 3600 });
return response;
},
};
Best Practices
Performance
- •Minimize Cold Starts: Keep Workers lightweight (<1MB bundled)
- •Use Bindings Over Fetch: Direct bindings are faster than HTTP calls
- •Edge Caching: Leverage KV and Cache API for frequently accessed data
- •Batch Operations: Use D1 batch for multiple queries
- •Stream Large Responses: Use
Response.bodystreams for large files
Security
- •Secrets Management: Use
wrangler secretfor API keys - •Environment Isolation: Separate production/staging/development
- •Input Validation: Sanitize user input
- •Rate Limiting: Use KV or Durable Objects for rate limiting
- •CORS: Configure proper CORS headers
Cost Optimization
- •R2 for Large Files: Zero egress fees vs S3
- •KV for Caching: Reduce D1/R2 requests
- •Request Deduplication: Cache identical requests
- •Efficient Queries: Index D1 tables properly
- •Monitor Usage: Use Cloudflare Analytics
Development Workflow
- •Local Development: Use
wrangler devfor testing - •Type Safety: Use TypeScript with
@cloudflare/workers-types - •Testing: Use Vitest with
unstable_dev() - •CI/CD: GitHub Actions with
cloudflare/wrangler-action - •Gradual Deployments: Use percentage-based rollouts
Common Patterns
API Gateway
import { Hono } from 'hono';
const app = new Hono();
app.get('/api/users/:id', async (c) => {
const user = await c.env.DB.prepare('SELECT * FROM users WHERE id = ?')
.bind(c.req.param('id'))
.first();
return c.json(user);
});
app.post('/api/users', async (c) => {
const { name, email } = await c.req.json();
await c.env.DB.prepare('INSERT INTO users (name, email) VALUES (?, ?)').bind(name, email).run();
return c.json({ success: true }, 201);
});
export default app;
Image Transformation
export default {
async fetch(request: Request, env: Env) {
const url = new URL(request.url);
const imageKey = url.pathname.replace('/images/', '');
// Get from R2
const object = await env.R2_BUCKET.get(imageKey);
if (!object) {
return new Response('Not found', { status: 404 });
}
// Transform with Cloudflare Images
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType || 'image/jpeg',
'Cache-Control': 'public, max-age=86400',
'cf-image-resize': JSON.stringify({
width: 800,
height: 600,
fit: 'cover',
}),
},
});
},
};
Rate Limiting (KV)
async function rateLimit(ip: string, env: Env): Promise<boolean> {
const key = `ratelimit:${ip}`;
const limit = 100; // requests per minute
const window = 60; // seconds
const current = await env.KV.get(key);
const count = current ? parseInt(current) : 0;
if (count >= limit) {
return false; // Rate limit exceeded
}
await env.KV.put(key, (count + 1).toString(), {
expirationTtl: window,
});
return true;
}
export default {
async fetch(request: Request, env: Env) {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
if (!(await rateLimit(ip, env))) {
return new Response('Rate limit exceeded', { status: 429 });
}
return new Response('OK');
},
};
Scheduled Jobs
# wrangler.toml [triggers] crons = ["0 0 * * *"] # Daily at midnight
export default {
async scheduled(event: ScheduledEvent, env: Env) {
// Cleanup old sessions
const sessions = await env.KV.list({ prefix: 'session:' });
for (const key of sessions.keys) {
const session = await env.KV.get(key.name, 'json');
if (session.expiresAt < Date.now()) {
await env.KV.delete(key.name);
}
}
},
};
Troubleshooting
Common Issues
"Module not found" errors
- •Ensure dependencies are in
package.json - •Run
npm installbefore deploying - •Check compatibility_date in wrangler.toml
Database connection errors (D1)
- •Verify database_id in wrangler.toml
- •Check database exists:
wrangler d1 list - •Run migrations:
wrangler d1 execute DB --file=schema.sql
KV not found errors
- •Create namespace:
wrangler kv:namespace create MY_KV - •Add binding to wrangler.toml
- •Deploy after configuration changes
Cold start timeout
- •Reduce bundle size (<1MB ideal)
- •Remove unnecessary dependencies
- •Use dynamic imports for large libraries
CORS errors
- •Add CORS headers to responses:
typescript
return new Response(data, { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }, });
Deployment fails
- •Check wrangler version:
wrangler --version - •Verify authentication:
wrangler whoami - •Review build errors in console output
Debugging
# Real-time logs wrangler tail # Local debugging with breakpoints wrangler dev --local # Remote debugging wrangler dev --remote # Check deployment status wrangler deployments list
Decision Matrix
| Need | Choose |
|---|---|
| Sub-millisecond reads | KV |
| SQL queries | D1 |
| Large files (>25MB) | R2 |
| Real-time WebSockets | Durable Objects |
| Async background jobs | Queues |
| ACID transactions | D1 |
| Strong consistency | Durable Objects |
| Zero egress costs | R2 |
| AI inference | Workers AI |
| Static site hosting | Pages |
| Serverless functions | Workers |
| Multi-provider AI | AI Gateway |
Framework-Specific Guides
Next.js
- •Use
@cloudflare/next-on-pagesadapter - •Configure
next.config.jsfor edge runtime - •Deploy via
wrangler pages deploy
Remix
- •Use official Cloudflare template
- •Configure
server.tsfor Workers - •Access bindings via
context.cloudflare.env
Astro
- •Use
@astrojs/cloudflareadapter - •Enable SSR in
astro.config.mjs - •Access env via
Astro.locals.runtime.env
SvelteKit
- •Use
@sveltejs/adapter-cloudflare - •Configure in
svelte.config.js - •Access platform via
event.platform.env
Resources
- •Documentation: https://developers.cloudflare.com
- •Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/
- •Discord Community: https://discord.cloudflare.com
- •Examples: https://developers.cloudflare.com/workers/examples/
- •GitHub: https://github.com/cloudflare
- •Status Page: https://www.cloudflarestatus.com
Implementation Checklist
Workers Setup
- • Install Wrangler CLI (
npm install -g wrangler) - • Login to Cloudflare (
wrangler login) - • Create project (
wrangler init) - • Configure wrangler.toml
- • Add environment variables/secrets
- • Test locally (
wrangler dev) - • Deploy (
wrangler deploy)
Storage Setup (as needed)
- • Create D1 database and apply schema
- • Create KV namespace
- • Create R2 bucket
- • Configure Durable Objects
- • Set up Queues
- • Add bindings to wrangler.toml
Pages Setup
- • Connect Git repository or use CLI
- • Configure build settings
- • Set environment variables
- • Add Pages Functions (if needed)
- • Deploy and test
Production Checklist
- • Set up custom domain
- • Configure DNS records
- • Enable SSL/TLS
- • Set up monitoring/analytics
- • Configure rate limiting
- • Implement error handling
- • Set up CI/CD pipeline
- • Test gradual deployments
- • Document rollback procedure
- • Configure logging/observability