- Example NodeJS/React Consumer - playwright (BYO Adapter)
This is an example of a React "Product" API consumer that uses Playwright & Pactflow and GitHub Actions to generate and publish Pact consumer contracts.
It performs pre-deployment cross-compatibility checks to ensure that it is compatible with specified providers using the Bi-Directional contract capability of Pactflow.
See the full Pactflow Bi-Directional Workshop for which this can be substituted in as the "consumer".
Unlike the Pact-native consumer examples, this project has no Pact mock-provider library in the loop. Instead it demonstrates the "bring your own adapter" pattern:
- It's a React app implementing a "Product" website, built with Vite and TypeScript.
- Playwright drives the app end-to-end in a real browser and intercepts every
outbound request to the provider API with
page.route(). test/playwrightSerialiser.tsis a small, hand-written adapter — not a Pact library — that converts each intercepted request/response exchange into a Pact interaction and appends it to a contract file underpacts/.- Interactions are deduplicated by their generated
description(method, path, status and query string): the first interaction with a given description wins, later ones with the same description are dropped. PasskeepDupeDescs: trueto keep every interaction instead, including duplicates — useful when the same method/path/status/query legitimately produces different response bodies (e.g. pagination or randomised data) and each variant needs to appear in the contract. - A small
AUTOGEN_HEADER_BLOCKLISTstrips headers that Playwright or the browser attach and that carry no contract meaning (user-agent,sec-fetch-*,accept-encoding,date,connection,cache-control,pragma, and similar), so the published contract only asserts on headers the application itself cares about.
- Interactions are deduplicated by their generated
Because the contract is assembled by hand rather than recorded by a Pact mock server, this example is a template for teams using an HTTP testing tool that has no native Pact integration: the same pattern — intercept, transform, write — works for any framework that can intercept outbound requests.
In the following diagram, You can see how the consumer testing process works - it's the same as the current Pact process.
When we call "can-i-deploy" the cross-contract validation process kicks off on Pactflow, to ensure any consumer consumes a valid subset of the OAS for the provider.
The project uses a Makefile to simulate a very simple build pipeline with two stages - test and deploy.
When you run the CI pipeline (see below for doing this), the pipeline should perform the following activities (simplified):
- Test
- Run tests (including the pact tests that generate the contract)
- Publish pacts, tagging the consumer version with the name of the current branch
- Check if we are safe to deploy to Production with
can-i-deploy(ie. has the cross-contract validation has been successfully performed)
- Deploy (only from <main|master>)
- Deploy app to Production
- Record the Production deployment in the Pact Broker
This project is currently compatible with the following provider(s):
- pactflow-example-bi-directional-provider-dredd
- pactflow-example-bi-directional-provider-restassured
- pactflow-example-bi-directional-provider-postman
See Environment variables on how to set these up.
Software:
- Node.js 24 (LTS) or later
- Tools listed at: https://docs.pactflow.io/docs/workshops/ci-cd/set-up-ci/prerequisites/
- A pactflow.io account with an valid API token
Install dependencies with npm ci (or make install, which does the same
thing).
To be able to run some of the commands locally, you will need to export the following environment variables into your shell, or copy .env.example to .env and fill it in:
VITE_API_BASE_URL: the base URL of the provider API. Playwright intercepts every request to this origin, so nothing actually listens here during a test run — but the value must matchsrc/api.ts, the specs, the Makefile and CI, or interception silently misses and no contract is written. Defaults tohttp://localhost:8080.PACT_BROKER_TOKEN: a valid API token for PactflowPACT_BROKER_BASE_URL: a fully qualified domain name with protocol to your pact broker e.g. https://testdemo.pactflow.io
Set PACT_PROVIDER to one of the following
PACT_PROVIDER=pactflow-example-bi-directional-provider-dredd: Dredd - (https://github.com/pactflow/example-bi-directional-provider-dredd)PACT_PROVIDER=pactflow-example-bi-directional-provider-postman: Postman - (https://github.com/pactflow/example-bi-directional-provider-postman)PACT_PROVIDER=pactflow-example-bi-directional-provider-restassured: Rest Assured - (https://github.com/pactflow/example-bi-directional-provider-restassured)
NOTE: Playwright tests are located in ./test. See below for how to start
Playwright, generate the consumer contract, and publish the contract to
Pactflow.
make install- install project dependencies
Run each step separately
make test_and_publish- runs the Playwright tests (which generate the contract) and publishes the generated pact(s) to Pactflow- This will perform the following 2 calls
make testmake publish_pacts
- This will perform the following 2 calls
make can_i_deploy- runs can-i-deploy to check if its safe to deploy the consumermake deploy- deploys the app and records deployment
or run the whole lot in one go
make ci- run the CI process, but locally (uses Docker by default)
Installing alternate pact CLI tools.
If you don't have docker, you can use one of the ruby tools. The standalone, doesn't require that you install Ruby on your host machine.
make install-pact-ruby-cli- installs the pact ruby CLI toolmake install-pact-ruby-standalone- installs the pact standalone CLI depending on your platform
Using alternate pact CLI tools.
PACT_TOOL=docker make ci- run the CI process, using the pact Docker CLI toolPACT_TOOL=ruby_standalone make ci- run the CI process, using the pact standalone CLI toolPACT_TOOL=ruby_cli make ci- run the CI process, using the pact ruby CLI tool
Outside of the Makefile, the npm scripts are:
npm run dev- start the Vite dev server on its own, for manual poking aroundnpm test- run the Playwright suite. It starts the dev server itself (via Playwright'swebServeroption), runs every spec, and writes the generated contract(s) topacts/. There is no separate "start the app" step.npm run test:report- open the HTML report from the lastnpm testrunnpm run build- type-check and build the production bundle with Vitenpm run preview- preview the production build locallynpm run type-check-tsc --buildwith no emitnpm run lint/npm run lint:fix- Biome lint, with an autofix variantnpm run format/npm run format:fix- Biome format check/writenpm run check/npm run check:fix- Biome lint + format together
Look at one of the tests, e.g. test/productByQuery.spec.ts.
- Import
API_BASE_URL,PACTICIPANTandPROVIDERfrom./pactOptions, andtransformPlaywrightMatchToPactfrom./playwrightSerialiser. - Call it inside your Playwright route handler, after fulfilling the route.
import { API_BASE_URL, PACTICIPANT, PROVIDER } from "./pactOptions";
import { transformPlaywrightMatchToPact } from "./playwrightSerialiser";
await page.route(`${API_BASE_URL}/products?id=2`, async (route) => {
await route.fulfill({
status: 200,
body: JSON.stringify(testData),
headers: { "Content-Type": "application/json" },
});
await transformPlaywrightMatchToPact(route, {
pacticipant: PACTICIPANT,
provider: PROVIDER,
});
});The makefile has been configured to run on Unix/Windows and MacOS based systems, and tested against Github Actions
They can be run locally on Unix/Windows and MacOS, or on Windows via WSL2 or a shell with bash.
- Consumer Side Bi-Directional Contract Testing Guide
- Provider Side Bi-Directional Contract Testing Guide
- TBC
Reach out via a GitHub Issue, or reach us over in the Pact foundation Slack

