Skip to content

Deploying a Cloudflare Worker to Forward Traffic to a REST API

This guide explains how to create and deploy a Cloudflare Worker that listens for incoming POST requests containing a uuid, enriches the request with metadata, and forwards it to a specified backend REST API.

What You’ll Do

  • Create a Cloudflare Worker using the dashboard UI.
  • Replace the default code with a custom forwarding script.
  • Deploy the Worker to production.

Step 1: Log in to Cloudflare

Step 2: Navigate to Workers & Pages

  • In the left-hand sidebar, click Workers & Pages.
  • Click the Create application button.

Step 3: Choose Create Worker

  • Select the Worker tab (not Pages).
  • Click Create Worker to proceed.

Step 4: Replace the Default Code

Once you're in the online editor:

1. Delete the sample code in the editor.

2. Paste the custom Cloudflare Worker script (provided below).

Text Only
addEventListener("fetch", event => {
  event.respondWith(handleRequest(event));
});

const config = {
  restProxyUrl: "http://rest-proxy.netfein.com:8084/topics/test-claudflare",
  apiCatalogId: "fdbcbf71-af18-4887-98fa-3bc670754455",
  source: "claudflare"
};

async function handleRequest(event) {
  const request = event.request;

  try {
    const reqClone = request.clone();
    const url = new URL(request.url);
    const method = request.method;
    const ip = request.headers.get("CF-Connecting-IP") || "unknown";
    const time = Math.floor(Date.now() / 1000).toString();
    const path = url.pathname;
    const requestHeaders = extractHeaders(reqClone.headers);
    const requestBody = await readTruncatedBody(reqClone);

    const response = await fetch(request);
    const responseClone = response.clone();

    const statusCode = response.status;
    const status = response.statusText;
    const contentType = responseClone.headers.get("Content-Type") || "";
    const responseHeaders = extractHeaders(responseClone.headers);
    const responseBody = await readTruncatedBody(responseClone);

    const payload = {
      path,
      ip,
      time,
      statusCode,
      type: contentType,
      status,
      apiCatalogId: config.apiCatalogId,
      source: config.source,
      method,
      requestHeaders: JSON.stringify(requestHeaders),
      responseHeaders: JSON.stringify(responseHeaders),
      requestPayload: requestBody,
      responsePayload: responseBody
    };

    event.waitUntil(
      (async () => {
        try {
          await sendToRestProxy(payload);
        } catch (err) {
          console.error("Failed to send to REST proxy:", err.message);
        }
      })()
    );

    return response;
  } catch (err) {
    console.error("Request handling error:", err.message);
    return genErrorPage(err);
  }
}

function extractHeaders(headers) {
  const result = {};
  for (const [key, value] of headers.entries()) {
    result[key] = value;
  }
  return result;
}

async function readTruncatedBody(stream) {
  try {
    const text = await stream.text();
    return text.length > 10000 ? text.slice(0, 10000) + "...(truncated)" : text;
  } catch (e) {
    console.warn("Body read error:", e.message);
    return "";
  }
}

async function sendToRestProxy(payload) {
  const body = {
    records: [{ value: payload }]
  };

  // Timeout controller setup
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 seconds timeout

  try {
    const response = await fetch(config.restProxyUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/vnd.kafka.json.v2+json",
        "Accept": "application/vnd.kafka.v2+json"
      },
      body: JSON.stringify(body),
      signal: controller.signal
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      const errorText = await response.text();
      console.error(`REST proxy error ${response.status}: ${errorText}`);
      return;
    }

  } catch (err) {
    clearTimeout(timeoutId);
    console.error("REST proxy fetch failed:", err.message);
    return;
  }
}

function genErrorPage(err) {
  const message = err?.message || "Unknown error";
  const stack = err?.stack || "";

  const html = `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>500 Internal Server Error</title>
        <style>
          body { font-family: Arial, sans-serif; background: #f2f2f2; color: #333; padding: 2em; }
          h1 { color: #c00; }
          pre { background: #eee; padding: 1em; border-radius: 5px; overflow-x: auto; }
        </style>
      </head>
      <body>
        <h1>500 Internal Server Error</h1>
        <p><strong>Error:</strong> ${escapeHtml(message)}</p>
        <pre>${escapeHtml(stack)}</pre>
      </body>
    </html>
  `;

  return new Response(html, {
    status: 500,
    headers: { "Content-Type": "text/html; charset=utf-8" }
  });
}

function escapeHtml(text) {
  return String(text)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}
3. Update the following variables in the script:

  • restProxyUrl: The URL of the REST API endpoint that forwards data to the relevant Kafka topic.

  • apiCatalogId: The application ID associated with the domain, defined earlier in ApiFort.

Step 5: Save and Deploy

  • Click the Save and Deploy button in the top-right corner of the editor.