Form Handling

Handle HTML forms and file uploads.

URL-Encoded Forms

Standard HTML form with application/x-www-form-urlencoded:

export default {
  async fetch(request: Request): Promise<Response> {
    if (request.method === 'POST') {
      const form = await request.formData();
      const name = form.get('name');
      const email = form.get('email');

      return Response.json({ name, email });
    }

    // Show form
    const html = `
      <form method="POST">
        <input name="name" placeholder="Name" required>
        <input name="email" type="email" placeholder="Email" required>
        <button type="submit">Submit</button>
      </form>
    `;

    return new Response(html, {
      headers: { 'Content-Type': 'text/html' }
    });
  }
};

JSON Body

API endpoint accepting JSON:

export default {
  async fetch(request: Request): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    const contentType = request.headers.get('content-type') || '';

    if (!contentType.includes('application/json')) {
      return new Response('Expected JSON', { status: 400 });
    }

    const body = await request.json();

    // Process body
    return Response.json({
      received: body,
      timestamp: Date.now()
    });
  }
};

File Upload

Handle multipart/form-data with files:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method === 'POST') {
      const form = await request.formData();
      const file = form.get('file') as File;

      if (!file) {
        return new Response('No file uploaded', { status: 400 });
      }

      // Read file content
      const content = await file.arrayBuffer();

      // Save to storage (requires Storage binding)
      await env.STORAGE.put(file.name, new Uint8Array(content));

      return Response.json({
        name: file.name,
        size: file.size,
        type: file.type
      });
    }

    const html = `
      <form method="POST" enctype="multipart/form-data">
        <input name="file" type="file" required>
        <button type="submit">Upload</button>
      </form>
    `;

    return new Response(html, {
      headers: { 'Content-Type': 'text/html' }
    });
  }
};

Validation

Validate form data before processing:

interface ContactForm {
  name: string;
  email: string;
  message: string;
}

function validate(data: FormData): ContactForm | null {
  const name = data.get('name')?.toString().trim();
  const email = data.get('email')?.toString().trim();
  const message = data.get('message')?.toString().trim();

  if (!name || name.length < 2) return null;
  if (!email || !email.includes('@')) return null;
  if (!message || message.length < 10) return null;

  return { name, email, message };
}

export default {
  async fetch(request: Request): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    const form = await request.formData();
    const data = validate(form);

    if (!data) {
      return Response.json({ error: 'Invalid form data' }, { status: 400 });
    }

    // Process valid data
    return Response.json({ success: true, data });
  }
};