Playwright Backend MocksRun the real app. Mock only the outside world.
Good e2e tests cover your UI and your server — then fake Stripe, email, and every other third party at the boundary. Playwright can do the browser half. We make the server half just as easy.
Your UI and server stay real. backendMocks.route() targets the outbound HTTP your Node process makes — the calls that never show up in the browser Network tab.
Add one startup call. Under the hood it uses @mswjs/interceptors to catch outbound HTTP (and globalThis.WebSocket), so you configure mocks in Playwright and write the rest of your app exactly as you otherwise would — no test-only branches, wrappers, or dependency-injection seams for Stripe, email, and friends.
ts
import { startBackendMocks } from "@playwright-backend-mocks/node";if (process.env.PLAYWRIGHT_BACKEND_MOCKS_PROXY_URL !== undefined) { await startBackendMocks({ proxyUrl: process.env.PLAYWRIGHT_BACKEND_MOCKS_PROXY_URL, clientId: "api-server", });}// The rest of your app is unchanged — keep using fetch, http, axios, etc.
Because interception happens at the lowest level through @mswjs/interceptors, this library can capture and mock requests from virtually every Node HTTP client and the frameworks and SDKs built on top of them — without rewriting how your app talks to the network.
fetch
http
axios
Next.js
Nuxt.js
Stripe
Got
node-fetch
Twilio
GraphQL
Supabase
Firebase
OpenAI
Algolia
Express
Fastify
NestJS
RedwoodJS
SvelteKit
Astro
Apollo
urql
tRPC
Undici
Ky
Koa
Hapi
Remix
Not magic — a proxy your Playwright tests control
Your app still runs for real. We intercept the HTTP it sends to the outside world, then let the test decide what happens next.
Start a proxy — a small local process between your Node app and Playwright. It matches routes, returns decisions, and exposes request history over REST.
Route Node HTTP through it — startBackendMocks() catches outbound fetch / http / https calls and sends them to the proxy instead of going straight to Stripe, email, etc.
Control it from Playwright — your test calls backendMocks.route(...). When the app makes a matching request, the handler runs in Playwright — fulfill a mock, continue upstream, or abort — and Node gets that result.
Unmatched requests pass through to the real network. Outside tests, with no proxy URL set, the Node agent is a no-op.
While the proxy is running, inspect HTTP and WebSocket traffic — including which test owned it and what action it took — via the optional dashboard or the REST API:
GET /api/history — HTTP timeline
GET /api/ws — WebSocket connections and events
GET /api/history/:id/har — download one HTTP request as HAR (for routeFromHAR)
Local coding agents can use the same REST surface; see Observability.