Skip to main content

infrastructure-cloudflare-workers

Building serverless APIs at the edge (low latency globally)

Quellinformationen

Repository
tools-only/X-Skills
Letzte Quellaktivität
1. März 2026 um 03:37
Erkannte Sprache von SKILL.md
Englisch
Sterne
7
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
infrastructure-cloudflare-workers
description
Building serverless APIs at the edge (low latency globally)
# Cloudflare Workers **Scope**: Edge computing with Cloudflare Workers - Edge functions, KV storage, Durable Objects, R2 **Lines**: 365 **Last Updated**: 2025-10-18 **Format Version**: 1.0 (Atomic) --- ## When to Use This Skill **Activate when**: - Building serverless APIs at the edge (low latency globally) - Implementing CDN logic and custom routing - A/B testing and feature flags - Authentication and authorization at the edge - API proxying and transformation - Static site generation with dynamic elements **Prerequisites**: - Cloudflare account (free tier available) - Node.js installed for Wrangler CLI - Wrangler CLI installed (`npm install -g wrangler`) - Basic JavaScript/TypeScript knowledge - Cloudflare domain (for production deployment) **Common scenarios**: - API endpoints with global low latency - Image resizing and optimization - Request/response manipulation - Rate limiting and bot protection - Geo-routing and localization - Edge-side rendering (ESR) --- ## Core Concepts ### 1. Basic Worker ```javascript // src/index.js - Simple worker export default { async fetch(request, env, ctx) { const url = new URL(request.url); // Simple routing if (url.pathname === '/api/hello') { return new Response(JSON.stringify({ message: 'Hello from the edge!', location: request.cf.city, country: request.cf.country }), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }); } // 404 response return new Response('Not Found', { status: 404 }); } }; ``` ### 2. Wrangler Configuration ```toml # wrangler.toml name = "my-worker" main = "src/index.js" compatibility_date = "2024-01-01" # Environment variables [vars] ENVIRONMENT = "production" API_VERSION = "v1" # KV Namespace bindings [[kv_namespaces]] binding = "CACHE" id = "abcdef1234567890abcdef1234567890" # Durable Object bindings [[durable_objects.bindings]] name = "COUNTER" class_name = "Counter" # R2 bucket bindings [[r2_buckets]] binding = "ASSETS" bucket_name = "my-assets" # Secrets (set with: wrangler secret put SECRET_NAME) # Access via env.SECRET_NAME # Routes (custom domain) [[routes]] pattern = "api.example.com/*" zone_name = "example.com" # Workers for Platforms (optional) [dispatch_namespaces] binding = "DISPATCHER" namespace = "my-namespace" ``` ### 3. Request/Response Handling ```typescript // src/index.ts - TypeScript worker interface Env { CACHE: KVNamespace; API_KEY: string; ENVIRONMENT: string; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const url = new URL(request.url); // CORS handling if (request.method === 'OPTIONS') { return handleCORS(); } // Authentication const apiKey = request.headers.get('X-API-Key'); if (apiKey !== env.API_KEY) { return new Response('Unauthorized', { status: 401 }); } // Route handling switch (url.pathname) { case '/api/data': return handleData(request, env, ctx); case '/api/upload': return handleUpload(request, env); default: return new Response('Not Found', { status: 404 }); } } }; function handleCORS(): Response { return new Response(null, { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, X-API-Key', 'Access-Control-Max-Age': '86400', } }); } async function handleData(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const cacheKey = new URL(request.url).pathname; // Check cache first const cached = await env.CACHE.get(cacheKey, 'json'); if (cached) { return Response.json(cached, { headers: { 'X-Cache': 'HIT' } }); } // Fetch data const data = await fetchData(); // Cache in background ctx.waitUntil( env.CACHE.put(cacheKey, JSON.stringify(data), { expirationTtl: 3600 // 1 hour }) ); return Response.json(data, { headers: { 'X-Cache': 'MISS' } }); } async function fetchData() { // Fetch from origin API const response = await fetch('https://api.example.com/data'); return response.json(); } ``` ### 4. KV Storage ```typescript // KV operations interface Env { CACHE: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const key = url.searchParams.get('key'); if (request.method === 'GET') { // Get value const value = await env.CACHE.get(key); if (!value) { return new Response('Not Found', { status: 404 }); } return new Response(value); } if (request.method === 'POST') { // Put value with expiration const value = await request.text(); await env.CACHE.put(key, value, { expirationTtl: 3600, // Seconds metadata: { timestamp: Date.now() } }); return new Response('Stored', { status: 201 }); } if (request.method === 'DELETE') { // Delete value await env.CACHE.delete(key); return new Response('Deleted', { status: 204 }); } // List keys with prefix if (url.pathname === '/list') { const prefix = url.searchParams.get('prefix') || ''; const list = await env.CACHE.list({ prefix, limit: 100 }); return Response.json(list.keys); } return new Response('Method Not Allowed', { status: 405 }); } }; ``` ### 5. Durable Objects ```typescript // src/counter.ts - Durable Object export class Counter { state: DurableObjectState; value: number; constructor(state: DurableObjectState, env: Env) { this.state = state; this.value = 0; } async initialize() { // Load state from storage const stored = await this.state.storage.get('value'); this.value = stored || 0; } async fetch(request: Request): Promise<Response> { await this.initialize(); const url = new URL(request.url); if (url.pathname === '/increment') { this.value++; await this.state.storage.put('value', this.value); return Response.json({ value: this.value }); } if (url.pathname === '/decrement') { this.value--; await this.state.storage.put('value', this.value); return Response.json({ value: this.value }); } if (url.pathname === '/get') { return Response.json({ value: this.value }); } return new Response('Not Found', { status: 404 }); } } // src/index.ts - Use Durable Object export { Counter } from './counter'; interface Env { COUNTER: DurableObjectNamespace; } export default { async fetch(request: Request, env: Env): Promise<Response> { // Get Durable Object instance by ID const id = env.COUNTER.idFromName('global-counter'); const stub = env.COUNTER.get(id); // Forward request to Durable Object return stub.fetch(request); } }; ``` ### 6. R2 Storage ```typescript // R2 bucket operations interface Env { ASSETS: R2Bucket; } export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const key = url.pathname.slice(1); // Remove leading / if (request.method === 'GET') { // Get object const object = await env.ASSETS.get(key); if (!object) { return new Response('Not Found', { status: 404 }); } const headers = new Headers(); object.writeHttpMetadata(headers); headers.set('etag', object.httpEtag); return new Response(object.body, { headers }); } if (request.method === 'PUT') { // Upload object await env.ASSETS.put(key, request.body, { httpMetadata: { contentType: request.headers.get('content-type') || 'application/octet-stream', }, customMetadata: { uploadedBy: 'worker', timestamp: new Date().toISOString(), } }); return new Response('Uploaded', { status: 201 }); } if (request.method === 'DELETE') { // Delete object await env.ASSETS.delete(key); return new Response('Deleted', { status: 204 }); } return new Response('Method Not Allowed', { status: 405 }); } }; ``` --- ## Patterns ### Edge Caching ```typescript // Cache API with edge caching export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const cache = caches.default; // Try to get from cache let response = await cache.match(request); if (response) { return response; } // Fetch from origin response = await fetch(request); // Cache successful responses if (response.ok) { const responseToCache = response.clone(); ctx.waitUntil(cache.put(request, responseToCache)); } return response; } }; ``` ### Request Rewriting ```typescript // Rewrite requests to different origins export default { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); // API proxy if (url.pathname.startsWith('/api/')) { url.hostname = 'api.backend.com'; url.pathname = url.pathname.replace('/api/', '/v1/'); const modifiedRequest = new Request(url.toString(), { method: request.method, headers: request.headers, body: request.body, }); return fetch(modifiedRequest); } // Default to static assets url.hostname = 'static.example.com'; return fetch(url.toString()); } }; ``` ### A/B Testing ```typescript // A/B testing with cookie-based variants export default { async fetch(request: Request): Promise<Response> { const url = new URL(request.url); // Check for existing variant cookie let variant = getCookie(request, 'ab_variant'); if (!variant) { // Assign random variant variant = Math.random() < 0.5 ? 'A' : 'B'; } // Fetch variant-specific content const response = await fetch(`https://origin.com/${variant}${url.pathname}`); // Set variant cookie const newResponse = new Response(response.body, response); newResponse.headers.set('Set-Cookie', `ab_variant=${variant}; Path=/; Max-Age=86400`); return newResponse; } }; function getCookie(request: Request, name: string): string | null { const cookies = request.headers.get('Cookie'); if (!cookies) return null; const cookie = cookies.split(';').find(c => c.trim().startsWith(`${name}=`));
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen