Lead Capture Cloudflare Worker

$29 Development

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

  1. npx wrangler init lead-capture
  2. Copy generated Worker code to src/index.js
  3. Create KV namespace: npx wrangler kv:namespace create RATE_LIMIT_KV
  4. Set secrets: npx wrangler secret put FIREBASE_SERVICE_ACCOUNT_JSON
  5. Set secrets: npx wrangler secret put RESEND_API_KEY
  6. Deploy: npx wrangler deploy
  7. 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
🔒

Unlock This Skill

Get full access to this skill for a one-time purchase of $29.