Skip to Content
FrameworkUpstream & mock

Upstream proxy & mock service

An app can sit in front of an existing API (legacy ERP, partner service) and replace it one endpoint at a time, mock what is not ready, or simulate failures — while every request is logged.

import { env } from "cloudflare:workers"; // app/upstream.ts — one object or a list; prefix is the app path (with /api) export default [ { prefix: "/api/erp", origin: "https://erp.congty.vn/api" }, // auth: "forward" (default) { prefix: "/api/pay", origin: "https://pay.partner.vn", auth: "service", headers: { authorization: `Bearer ${env.PAY_TOKEN}` } }, // token from Secrets (env_set) ];
  • Order: a Manh handler that matches (method + path) always wins; a request under a prefix with no handler is forwarded to origin + <rest of the path> (method, query, body, headers except cookie/host/x-ft-*; redirects passed through, set-cookie dropped). Outside every prefix → normal 404. Longest prefix first.
  • auth: "forward" passes the user’s Authorization header to the real system (the app has no login of its own). "service" drops it and sends headers instead (a service token from Secrets); the app then does its own DIY login.
  • Change an answer — in a handler: const res = await upstream(c) (same request) or upstream(c, { path: "/api/erp/orders/" + id, method: "GET" }), then return what you want.
  • Mock — return mock(c, data, { status?, delay? }) answers without calling the real system; logged as source mock. Switch mocks without a deploy with a $mock store value (D40) the owner edits in Dữ liệu → JSON:
    import { mock, upstream } from "app/std/upstream"; export default async function handler(c) { const mode = $mock?.["GET /api/erp/orders"] ?? "real"; // "real" | "mock" | "error500" | "slow3s" if (mode === "error500") return mock(c, { message: "ERP lỗi" }, { status: 500 }); if (mode === "slow3s") return mock(c, SAMPLE_ORDERS, { delay: 3000 }); if (mode === "mock") return mock(c, SAMPLE_ORDERS); return upstream(c); }
  • Traffic log: every /api request of a Durable Object app is written to ft_traffic (method, path, status, source handler | upstream | mock | error, timings, masked headers, bodies ≤ 8 KB, text/JSON only; authorization, cookie, x-api-key and fields named like password/token/secret/otp are masked). Kept 7 days / 20,000 rows. log: { bodies: false } on any upstream entry stops recording bodies. Owner: Vận hành → Lưu lượng; agent: logs({ type: "traffic" }) — write mocks and tests from recorded real responses.
  • Rules (check): origin must be a string literal https:// public domain — no IP, localhost, *.local/*.internal, manh.run or workers.dev hosts (upstream.origin-invalid, also enforced at runtime); app/upstream.ts without an origin → upstream.origin-missing. API only — proxying whole websites (HTML) is not supported. Never hard-code the upstream token (code.hardcoded-secret).
  • The live preview does not call upstreams; test against the deployed app.
Last updated on