# Scenarist > Scenarist is a TypeScript testing library for Node.js apps. Your real server code runs in Playwright or Supertest tests (Express routes, Next.js Server Components, Route Handlers, Server Actions, middleware) while every external HTTP API it calls returns the response defined by the scenario that test selected. Tests switch scenarios at runtime by test ID, so parallel tests get different backend states from one running server. Built on MSW (Mock Service Worker). MIT licensed. Packages. Install one adapter plus `msw` (a required peer dependency). Import everything, including types, from the adapter; do not install `@scenarist/core` directly. - `@scenarist/express-adapter`: Express `^4.18 || ^5` - `@scenarist/nextjs-adapter`: Next.js `^14 || ^15 || ^16`. Import from `@scenarist/nextjs-adapter/app` (App Router) or `@scenarist/nextjs-adapter/pages` (Pages Router) - `@scenarist/playwright-helpers`: Playwright fixtures (dev dependency) How a test runs: 1. Scenarios are plain data: an object keyed by scenario ID, declared `as const satisfies ScenaristScenarios`. It must contain a `default` key. 2. `createScenarist({ enabled, scenarios })` returns the instance, or `undefined` when `enabled` is `false`, when the bundler (or `node --conditions=production`) resolves the `production` export condition, or, in the Express adapter, when `NODE_ENV` is `production`. Always guard with `if (scenarist)` or `scenarist?.`. 3. Each test gets a unique test ID. `switchScenario(page, 'scenarioId')` POSTs `{ scenario }` to the scenario endpoint with the `x-scenarist-test-id` header, and adds that header to every request the page makes. 4. MSW intercepts the server's outgoing HTTP requests. The test ID picks the active scenario. For any method and URL the active scenario does not cover, the `default` scenario's mocks apply. When the active scenario has a mock without `match` criteria for that method and URL, the default's mocks for it are ignored; when it has only mocks with `match` criteria, the default's mocks stay available as a fallback. Rules that are easy to get wrong: - Scenarios are declarative. Never put functions or callbacks in a scenario. Use `match`, `sequence`, `captureState` with `{{state.key}}` templates, `stateResponse`, and `afterResponse` instead. - A mock is `{ method, url, match?, response | sequence | stateResponse, captureState?, afterResponse? }`, with at most one of `response`, `sequence`, or `stateResponse`. `url` accepts an exact URL, path-to-regexp v6 parameters such as `:id`, `:id?`, and `:path+`, or a RegExp. Glob wildcards such as `/api/*` are not supported. - Selection: a mock whose `match` criteria pass beats a mock without criteria, and more specific criteria win (each matched body, header, query, or state key adds to the score). Among mocks without criteria, one with a `sequence` or `stateResponse` beats a plain `response`; within the same kind, the last one wins. - Next.js: create the instance once at module level (`export const scenarist = createScenarist(...)`) and import it wherever you need it. The adapter caches the first instance on `globalThis`, because Next.js can load the same module more than once; later calls return that cached instance and ignore their options (except `enabled: false`, which returns `undefined`). - Next.js: run scenario tests against `next dev`, which both example apps (Next.js 16) start from the Playwright `webServer` config with `next dev --webpack`; Next.js 14 and 15 have no `--webpack` flag. `next build` resolves the `production` export condition, so against `next start` the scenario route answers 405 and `switchScenario` throws. Next.js replaces `process.env.NODE_ENV` in server code with `'development'` or `'production'`, so do not gate `enabled` on `process.env.NODE_ENV === 'test'`; the example apps pass `enabled: true`. - Next.js: forward the test ID on every outgoing `fetch`. Spread `getScenaristHeaders(request)` in Route Handlers and API routes, or `getScenaristHeadersFromReadonlyHeaders(await headers())` in Server Components. Use `cache: 'no-store'` on those fetches. Express propagates the test ID automatically once `app.use(scenarist.middleware)` is registered before your routes. - Next.js App Router serves the scenario endpoint from `app/api/%5F%5Fscenario%5F%5F/route.ts`, which exports `POST` and `GET` as `scenarist?.createScenarioEndpoint()` (optional chaining, because the instance is `undefined` in production builds). The encoded underscores are needed because Next.js excludes folders that start with `_` from routing. The URL is `/api/__scenario__`, which is the Playwright helpers' default `scenaristEndpoint`. The debug state route follows the same pattern at `/api/__scenarist__/state`, so set `scenaristStateEndpoint: '/api/__scenarist__/state'` in the Playwright `use` config. Express serves `/__scenario__` and `/__scenarist__/state`, so Express projects set `scenaristEndpoint: '/__scenario__'`. - In Playwright, import `test` and `expect` from your own fixtures file (`export const test = withScenarios(scenarios)`), not from `@playwright/test`. Calls made with `page.request` do not carry the test ID automatically; pass the ID that `switchScenario` returns in an `x-scenarist-test-id` header. - Scenarist mocks HTTP requests made by the server process. It cannot mock database drivers, the file system, or WebSocket traffic. - Unmocked requests pass through to the real network unless `strictMode: true`, which answers them with `501`. `errorBehaviors` (`onNoMockFound`, `onSequenceExhausted`, `onMissingTestId`) default to `'ignore'`; `'warn'` logs through the configured `logger` and `'throw'` answers with `500` and a JSON body carrying the error `code`. ## Start here - [Quick Start](https://scenarist.io/getting-started/quick-start/): The three-step model (define scenarios, add the adapter, switch per test) and links to each framework guide - [Installation](https://scenarist.io/getting-started/installation/): Package names, install commands, subpath imports, and peer dependency ranges for every framework - [Why Scenarist?](https://scenarist.io/getting-started/why-scenarist/): The gap between unit and end-to-end tests, and what Scenarist can and cannot intercept - [How It Works](https://scenarist.io/concepts/how-it-works/): Execution model: test IDs, runtime scenario switching, and how intercepted requests are routed - [Testing Philosophy](https://scenarist.io/concepts/philosophy/): Test behaviour through the real server, mock only what you do not own ## Framework setup - [Express: Getting Started](https://scenarist.io/frameworks/express/getting-started/): Middleware setup, Supertest tests, and automatic test ID propagation via AsyncLocalStorage - [Next.js App Router: Getting Started](https://scenarist.io/frameworks/nextjs-app-router/getting-started/): Singleton setup, the `/api/__scenario__` route handler, Playwright fixtures, and header forwarding from Server Components and Route Handlers - [Next.js Pages Router: Getting Started](https://scenarist.io/frameworks/nextjs-pages-router/getting-started/): API route endpoints, header forwarding from API routes and getServerSideProps - [Testing React Server Components](https://scenarist.io/frameworks/nextjs-app-router/rsc/): Data fetching, streaming and Suspense, Server Actions, auth flows, and error boundaries - [RSC Troubleshooting](https://scenarist.io/frameworks/nextjs-app-router/rsc/troubleshooting/): Missing header forwarding, `page.request` without a test ID, Next.js fetch caching, sequences that never advance ## Writing scenarios - [Writing Scenarios Overview](https://scenarist.io/scenarios/overview/): Which scenario feature solves which problem - [Basic Structure](https://scenarist.io/scenarios/basic-structure/): Scenario and mock fields, HTTP methods, URL patterns, and response shape - [Default Scenarios](https://scenarist.io/scenarios/default-scenarios/): The required `default` scenario, partial overrides, and specificity-based mock selection - [Request Matching](https://scenarist.io/scenarios/request-matching/): Different responses for the same URL based on body, headers, or query - [Pattern Matching](https://scenarist.io/scenarios/pattern-matching/): `equals`, `contains`, `startsWith`, `endsWith`, `regex`, and native RegExp - [Response Sequences](https://scenarist.io/scenarios/response-sequences/): Ordered responses for polling and async jobs, with `repeat: 'last' | 'cycle' | 'none'` - [Stateful Mocks](https://scenarist.io/scenarios/stateful-mocks/): Capture values from requests with `captureState` and inject them into later responses with templates - [State-Aware Mocking](https://scenarist.io/scenarios/state-aware-mocking/): `stateResponse` conditions, `afterResponse.setState` transitions, and `match.state` - [Combining Features](https://scenarist.io/scenarios/combining-features/): Matching, sequences, and state together in one workflow - [TypeScript Patterns](https://scenarist.io/scenarios/typescript-patterns/): `as const satisfies ScenaristScenarios` for type-safe scenario IDs and autocomplete ## Testing - [Playwright Integration](https://scenarist.io/testing/playwright-integration/): `withScenarios` fixtures, `switchScenario`, `debugState`, `waitForDebugState`, and endpoint configuration - [Parallel Testing](https://scenarist.io/testing/parallel-testing/): How test ID isolation lets concurrent tests use different scenarios against one server - [Testing Best Practices](https://scenarist.io/testing/best-practices/): Organising scenarios, one switch per test, and anti-patterns to avoid - [Testing Apps with Database Access](https://scenarist.io/guides/testing-database-apps/): Scenarist cannot intercept database calls; the repository pattern and Testcontainers options ## Reference - [Endpoint APIs](https://scenarist.io/reference/api-endpoints/): Request and response shapes for the scenario switch, status, and debug state endpoints - [Ephemeral Endpoints](https://scenarist.io/reference/ephemeral-endpoints/): Why the scenario endpoints exist only when Scenarist is enabled, and how test ID isolation works - [Error Handling](https://scenarist.io/reference/errors/): Error codes and the `errorBehaviors` options - [Debugging with Logs](https://scenarist.io/reference/logging/): `createConsoleLogger` levels, categories, and formats for tracing mock selection - [Production Safety](https://scenarist.io/concepts/production-safety/): How conditional exports keep Scenarist and MSW out of production bundles, and how to verify it - [Verification Guide](https://scenarist.io/reference/verification/): Checks that confirm Scenarist is wired up correctly - [Architecture](https://scenarist.io/concepts/architecture/): The framework-agnostic hexagonal core and its adapters ## Documentation Sets - [Abridged documentation](https://scenarist.io/llms-small.txt): a compact version of the documentation for Scenarist, with non-essential content removed - [Complete documentation](https://scenarist.io/llms-full.txt): the full documentation for Scenarist - [Getting started and concepts](https://scenarist.io/_llms-txt/getting-started-and-concepts.txt): installation, quick start, how Scenarist works, philosophy, production safety, and architecture - [Writing scenarios](https://scenarist.io/_llms-txt/writing-scenarios.txt): every scenario feature: structure, defaults, request and pattern matching, sequences, stateful and state-aware mocks - [Express](https://scenarist.io/_llms-txt/express.txt): setting up and testing Express apps - [Next.js App Router](https://scenarist.io/_llms-txt/nextjs-app-router.txt): setting up Next.js App Router and testing React Server Components, streaming, Server Actions, and auth flows - [Next.js Pages Router](https://scenarist.io/_llms-txt/nextjs-pages-router.txt): setting up Next.js Pages Router with API routes and getServerSideProps - [Testing and reference](https://scenarist.io/_llms-txt/testing-and-reference.txt): Playwright integration, parallel testing, best practices, database apps, endpoint and error reference, logging, and verification - [Comparisons](https://scenarist.io/_llms-txt/comparisons.txt): how Scenarist compares with MSW, WireMock, Nock, Testcontainers, and Playwright route mocking ## Notes - The complete documentation includes all content from the official documentation - The content is automatically generated from the same source as the official documentation ## Optional - [GitHub repository](https://github.com/citypaul/scenarist): source code, issues, and changelogs - [Express example app](https://github.com/citypaul/scenarist/tree/main/apps/express-example): complete Express app with Supertest scenario tests - [Next.js App Router example app](https://github.com/citypaul/scenarist/tree/main/apps/nextjs-app-router-example): complete App Router app with Playwright scenario tests - [Next.js Pages Router example app](https://github.com/citypaul/scenarist/tree/main/apps/nextjs-pages-router-example): complete Pages Router app with Playwright scenario tests - [Roadmap](https://scenarist.io/roadmap/): planned features