Loading
Skip to content
RedRouterRedRouter
Dashboard ↗

Cloudflare Workers

Call RedRouter from a Cloudflare Worker with native fetch, keeping the API key in a secret binding. This local development recipe has not been deployed or live-tested against RedRouter.

Create a local Worker

In a new directory:

npm init -y
npm install --save-dev wrangler
mkdir src

Save wrangler.jsonc:

{
  "name": "redrouter-example",
  "main": "src/index.js",
  "compatibility_date": "2026-11-04"
}

Save .dev.vars and exclude it from Git:

REDROUTER_API_KEY="your-redrouter-api-key"
APP_TOKEN="a-separate-long-random-client-secret"

APP_TOKEN authenticates callers to this example Worker; it is different from your RedRouter key. This example is for trusted server callers, not a browser bundle. Before any public deployment, replace this gate with your application's access controls and store both values as Worker secrets.

Forward a bounded streaming request

Save src/index.js:

async function readBody(request, limit) {
  if (!request.body) throw new Error("empty");
  const reader = request.body.getReader();
  const chunks = [];
  let size = 0;
  try {
    for (;;) {
      const { done, value } = await reader.read();
      if (done) break;
      size += value.byteLength;
      if (size > limit) throw new Error("large");
      chunks.push(value);
    }
  } finally {
    await reader.cancel();
  }
  const bytes = new Uint8Array(size);
  let offset = 0;
  for (const chunk of chunks) {
    bytes.set(chunk, offset);
    offset += chunk.byteLength;
  }
  return JSON.parse(new TextDecoder().decode(bytes));
}

export default {
  async fetch(request, env) {
    if (!env.APP_TOKEN || !env.REDROUTER_API_KEY) {
      return new Response("Missing server configuration", { status: 503 });
    }
    if (request.headers.get("Authorization") !== `Bearer ${env.APP_TOKEN}`) {
      return new Response("Unauthorized", { status: 401 });
    }
    if (request.method !== "POST") {
      return new Response("Use POST", { status: 405 });
    }
    let body;
    try {
      body = await readBody(request, 16384);
    } catch {
      return new Response("Invalid or oversized JSON body", { status: 400 });
    }
    if (typeof body?.prompt !== "string" || !body.prompt.trim() || body.prompt.length > 4000) {
      return new Response("Supply a prompt of 1–4000 characters", { status: 400 });
    }
    try {
      const upstream = await fetch("https://redrouter.org/v1/chat/completions", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${env.REDROUTER_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          model: "red-cyber-1",
          messages: [{ role: "user", content: body.prompt }],
          max_tokens: 512,
          stream: true,
          stream_options: { include_usage: true },
        }),
        signal: AbortSignal.any([request.signal, AbortSignal.timeout(120000)]),
      });
      const headers = new Headers({
        "Content-Type": upstream.headers.get("Content-Type") ?? "application/json",
        "Cache-Control": "no-store",
      });
      for (const name of ["Retry-After", "x-request-id"]) {
        const value = upstream.headers.get(name);
        if (value !== null) headers.set(name, value);
      }
      return new Response(upstream.body, {
        status: upstream.status,
        headers,
      });
    } catch {
      return new Response("Generation connection failed", { status: 502 });
    }
  },
};

The endpoint and model are fixed; callers cannot turn it into an arbitrary proxy. The Worker returns the response stream without buffering the completion. HTTP errors retain RedRouter's status and body, with Retry-After and x-request-id forwarded when present; transport errors return 502.

Run locally and interpret output

npx wrangler dev

Wrangler prints the address of your local Worker. In another terminal, set WORKER_URL to that address and APP_TOKEN to the value in .dev.vars, then run:

export WORKER_URL="YOUR_LOCAL_WORKER_URL"
curl -N "$WORKER_URL" \
  -H "Authorization: Bearer $APP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Explain HTTP caching in one sentence."}'

This local request makes a billable RedRouter call. A successful response contains raw SSE data: events ending with [DONE]. Clients must parse that format, read terminal usage when present, and detect stream errors and truncation. An HTTP 200 alone does not prove the generation completed.

The example does not implement per-user budgets or rate limiting. Add both before serving multiple users; see production guidance. red-cyber-1 is text-only and supports tools and structured output, but this Worker exposes only a text prompt. Workers AI and Cloudflare AI Gateway are separate services and are not required for this direct HTTP call.

References: Worker fetch handlers and secret bindings.

↑ ↓ to select · Enter to open · Esc to close