API testing patterns for Playwright + TypeScript — resource class pattern (HTTP wrappers extending BasePage, domain folders mirroring REST namespaces, typed payload builders), the ApiListener pattern for capturing real responses by stateKey without mocking, optional SQL/stored-procedure bridge for test-data setup, and TOTP-based MFA enrollment. Use when seeding test data via API, asserting on API responses without mocking, building HTTP wrapper classes, capturing network responses during UI tests, or setting up test users with MFA.
API testing patterns for Playwright + TypeScript — resource class pattern (HTTP wrappers extending BasePage, domain folders mirroring REST namespaces, typed payload builders), the ApiListener pattern for capturing real responses by stateKey without mocking, optional SQL/stored-procedure bridge for test-data setup, and TOTP-based MFA enrollment. Use when seeding test data via API, asserting on API responses without mocking, building HTTP wrapper classes, capturing network responses during UI tests, or setting up test users with MFA.
when_to_use
Trigger phrases: "API test", "API helper", "api-listener", "ApiListener", "page.request", "capture response", "API resource class", "seed via API", "MFA", "TOTP", "executeLocalProcedure", "SQL setup", "test data via API". Auto-activates on files in api/.
paths
api/**/*.ts,tests/**/*.spec.ts,pages/**/*.ts
API Testing Patterns
Apply these patterns when seeding test data via API, asserting on real API responses without mocking, or building reusable HTTP wrappers for tests.
The Resource Class Pattern
Wrap each backend resource in a class. The folder structure mirrors the backend's REST namespace, so api/CRM/Users/... endpoints live in api/crm/users/users-resource.ts. Each class extends BasePage to inherit the same page.request context and credentials.
Same this.page so it shares the authenticated session.
Same env-backed credentials (this.BASE_URL, etc.).
Same expect import.
Same fixture lifecycle — instantiated via base-pages-fixture.ts like any other POM.
Typed params with optional fields
Use a TypeScript interface with optional fields, then filter undefined/empty before building the query string. Compile-time guidance on which params are valid, runtime flexibility to omit any.
Status assertion at the call site
Inline expect(response.status()).toBe(200) ensures API failures surface immediately at the seeding step, not as a confusing UI assertion later.
Optional baseUrl per method
If you may need to run helpers against multiple server instances, accept an optional baseUrl parameter that defaults to this.BASE_URL:
*-resource.ts for read-heavy collections (GET-dominant)
*-operations.ts for write-heavy entities (POST/PUT/DELETE-dominant)
custom/ for test-infra helpers that wrap procedure or SQL execution
integrations/ for third-party service wrappers (payment gateways, identity providers, analytics)
Each domain folder corresponds to a top-level REST namespace. New domains get a new folder.
The ApiListener Pattern — Capture Real Responses
Set up listeners before navigation, then trigger a UI action and read the captured response after. This avoids mocking entirely — you get structured access to real backend data for assertions.
Real responses, not synthetic — tests catch real backend regressions.
Decoupled — stateKey ties setup to assertion site by name. No tight coupling between intercept code and assertion code.
Transparent — the original response is re-fulfilled, so the app continues normally. The listener is invisible to the UI under test.
When mocking IS appropriate
Mock only external/third-party services (payment gateways, third-party identity providers, analytics endpoints). Never mock the primary application API — that defeats the purpose of end-to-end testing.
The SQL / Stored-Procedure Bridge (optional, project-specific)
If the application exposes a generic procedure-execution endpoint, wrap it for test-only DB manipulation. This avoids needing a direct DB connection from tests.
One bridge endpoint + one SQL passthrough procedure handles every test-infra DB need.
Idempotent procedure registration: if a stored procedure may or may not exist, check first via addProcedureAndGetId(name) before using.
DB-truth assertions: for asserting sort order or filter results, query the DB for the expected output and compare against the UI, rather than hardcoding the expected list. Adapts automatically when reference data changes.
Caveats
Test environments only. A raw SQL passthrough is a security risk in production.
Many backends don't have this. Skip the bridge entirely if your backend doesn't expose a procedure-execution endpoint; use direct database access via a Node.js DB client in a fixture instead.
MFA / TOTP Pattern
For tests against MFA-enabled users, generate TOTP codes inline:
The Authenticator is a self-contained RFC 6238 TOTP generator (no external library) initialized with the secret from the MFA setup API. Reuse the instance for subsequent code generations within the same 30-second window.