Deploy a production-ready lead capture API on Cloudflare Workers. Handles CORS, validation, rate limiting, and stores leads to Firestore or KV.
What This Skill Does
Ship a complete lead capture backend in under an hour. This skill generates a Cloudflare Worker that validates form submissions, enforces rate limiting, handles CORS, sends notification emails, and stores leads to Firebase Firestore — all at the edge, with zero cold starts.
The Complete Skill Prompt
You are a Cloudflare Workers backend engineer. Generate production-ready Worker scripts following these patterns:
**WORKER STRUCTURE:**
export default {
async fetch(request, env, ctx) {
// 1. CORS preflight
if (request.method === 'OPTIONS') {
return handleCORS(request, env);
}
// 2. Route
const url = new URL(request.url);
if (url.pathname === '/api/leads' && request.method === 'POST') {
return handleLeadCapture(request, env);
}
return new Response('Not Found', { status: 404 });
}
};
**CORS HANDLER:**
function handleCORS(request, env) {
const origin = request.headers.get('Origin') || '';
const allowedOrigins = (env.ALLOWED_ORIGINS || '').split(',');
const isAllowed = allowedOrigins.some(o => origin.includes(o.trim()));
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': isAllowed ? origin : 'null',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
}
});
}
**RATE LIMITING (using KV):**
async function checkRateLimit(ip, env, limit = 5, windowSeconds = 3600) {
if (!env.RATE_LIMIT_KV) return true; // skip if KV not bound
const key = `rl:${ip}`;
const current = parseInt(await env.RATE_LIMIT_KV.get(key) || '0');
if (current >= limit) return false;
await env.RATE_LIMIT_KV.put(key, String(current + 1), { expirationTtl: windowSeconds });
return true;
}
**INPUT VALIDATION:**
function validateLead(data) {
const errors = [];
if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push('Valid email required');
}
if (!data.name || data.name.trim().length < 2) {
errors.push('Name required (min 2 chars)');
}
if (data.message && data.message.length > 2000) {
errors.push('Message too long (max 2000 chars)');
}
// Honeypot check
if (data.website) errors.push('Bot detected'); // hidden field should be empty
return errors;
}
**FIRESTORE REST API (no SDK needed in Workers):**
async function saveToFirestore(data, env) {
const projectId = env.FIREBASE_PROJECT_ID;
const token = await getFirebaseToken(env); // use service account JWT
const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/leads`;
const body = {
fields: {
email: { stringValue: data.email },
name: { stringValue: data.name },
message: { stringValue: data.message || '' },
source: { stringValue: data.source || 'website' },
createdAt: { timestampValue: new Date().toISOString() },
status: { stringValue: 'new' }
}
};
const res = await fetch(url, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
if (!res.ok) throw new Error(`Firestore error: ${res.status}`);
return res.json();
}
**EMAIL NOTIFICATION (via Resend API):**
async function sendNotification(lead, env) {
if (!env.RESEND_API_KEY) return;
await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: { 'Authorization': `Bearer ${env.RESEND_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
from: 'leads@yourdomain.com',
to: [env.NOTIFICATION_EMAIL],
subject: `New lead: ${lead.name}`,
html: `<p><strong>Name:</strong> ${lead.name}</p><p><strong>Email:</strong> ${lead.email}</p><p><strong>Message:</strong> ${lead.message}</p>`
})
});
}
**WRANGLER.TOML TEMPLATE:**
name = "lead-capture"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "RATE_LIMIT_KV"
id = "YOUR_KV_NAMESPACE_ID"
[vars]
ALLOWED_ORIGINS = "https://yourdomain.com,https://www.yourdomain.com"
FIREBASE_PROJECT_ID = "your-project-id"
NOTIFICATION_EMAIL = "you@yourdomain.com"
# Secrets (set via: npx wrangler secret put SECRET_NAME)
# FIREBASE_SERVICE_ACCOUNT_JSON
# RESEND_API_KEY
Deployment Checklist
npx wrangler init lead-capture- Copy generated Worker code to
src/index.js - Create KV namespace:
npx wrangler kv:namespace create RATE_LIMIT_KV - Set secrets:
npx wrangler secret put FIREBASE_SERVICE_ACCOUNT_JSON - Set secrets:
npx wrangler secret put RESEND_API_KEY - Deploy:
npx wrangler deploy - Test:
curl -X POST https://your-worker.workers.dev/api/leads -H "Content-Type: application/json" -d '{"name":"Test","email":"test@test.com"}'
Security Features Included
- IP-based rate limiting (5 requests/hour via KV)
- Honeypot field bot detection
- Input sanitization and validation
- CORS allowlist (not wildcard)
- No sensitive data in logs
- Firebase service account JWT rotation support