Skip to content

Proxy

Package: @playwright-backend-mocks/proxy

The proxy coordinates WebSocket connections from Playwright workers and Node agents. It owns route claims, request decisions, in-memory history, and the read-only REST API.

CLI

Binary:

bash
playwright-backend-mocks-proxy [options]
FlagDefaultDescription
--host <host>127.0.0.1Bind host.
--port <port>4310Bind port.
--token <token>noneOptional shared connection token.
--history-limit <n>1000Number of recent HTTP history entries retained in memory.
--ws-history-limit <n>200Number of recent WebSocket connections retained in memory.
--history-capture <mode>allall, handled (test-acted only), or none.
--heartbeat-ms <ms>15000WebSocket ping interval.
--idle-timeout-ms <ms>60000Idle socket disconnect timeout.
--claim-timeout-ms <ms>5000How long to wait for Playwright route claim replies.
--log-level <level>infosilent, error, warn, info, or debug.
-h, --helpPrint help.

On startup the proxy prints the Node/Playwright WebSocket URL, REST base URL, and how to point the dashboard at the proxy. See Observability.

The process handles SIGINT and SIGTERM by stopping the server.

Playwright webServer

ts
{
  command:
    "playwright-backend-mocks-proxy --host 127.0.0.1 --port 4310 --claim-timeout-ms 5000",
  url: "http://127.0.0.1:4310/health",
  reuseExistingServer: !process.env.CI,
}

Programmatic API

ts
import {
  createProxyServer,
  createProxyConfig,
  DEFAULT_PROXY_CONFIG,
  type ProxyConfig,
  type ProxyServer,
} from "@playwright-backend-mocks/proxy";

const server = createProxyServer({
  port: 4310,
  logLevel: "debug",
});

await server.start();
console.log(server.url);
await server.stop();

ProxyConfig

ts
type LogLevel = "silent" | "error" | "warn" | "info" | "debug";

type HistoryCaptureMode = "all" | "handled" | "none";

interface ProxyConfig {
  readonly host: string;
  readonly port: number;
  readonly token: string | undefined;
  readonly historyLimit: number;
  readonly wsHistoryLimit: number;
  readonly historyCapture: HistoryCaptureMode;
  readonly heartbeatMs: number;
  readonly idleTimeoutMs: number;
  readonly claimTimeoutMs: number;
  readonly logLevel: LogLevel;
}

createProxyServer() merges overrides into DEFAULT_PROXY_CONFIG.

HTTP endpoints

MethodPathDescription
GET/healthLiveness plus package and protocol versions.
GET/api/historyRecent HTTP history (filterable).
GET/api/history/:idOne history entry.
GET/api/history/:id/harSingle-request HAR 1.2 (for routeFromHAR).
GET/api/wsRecent WebSocket connections (filterable).
GET/api/ws/:idOne WebSocket connection + event timeline.
GET/api/connectionsConnected Node agents and Playwright workers.
OPTIONSAPI pathsCORS preflight.
WebSocket/wsInternal coordinator transport (--token protects this; REST is local/read-only).

See REST API and Observability.

Ownership rules

For every HTTP request or globalThis.WebSocket connection:

Claim resultProxy decision
No test claimsPassthrough.
Exactly one test claimsSend the request/socket to that test's Playwright worker.
More than one test claimsFail with ambiguous_route.
A test does not answer before timeoutFail with claim_timeout.

Within one test, HTTP handler order is handled by the fixture: newest-first, with fallback() continuing the chain.

Mock the outside world. Keep the real app.