Skip to content

pactflow/example-bi-directional-consumer-msw

Repository files navigation

Example TypeScript/React Consumer - Mock Service Worker (BYO Adapter)

Build Can I deploy Status

Overview of Example

This is an example of a TypeScript/React "Product" API consumer that uses Mock-Service-Worker, Pact, 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".

Quick Start

Get up and running in 3 steps:

# 1. Install dependencies
npm install

# 2. Run tests and generate Pact contract
npm test

# 3. Check the generated contract
ls -la pacts/

You should see a Pact contract file generated in the pacts/ directory! This contract represents the consumer's expectations of the Provider API.

Tip

To see the full CI/CD workflow locally, run make fake_ci (requires Docker and these environment variables: PACT_BROKER_BASE_URL, PACT_BROKER_TOKEN)

Running with Makefile vs Native Tools

This project includes a Makefile for convenience, but you can also use npm and Docker commands directly:

Task Makefile Native Commands
Run tests make test npm test
Full CI pipeline make fake_ci Set environment variables and run commands in sequence

Tip

The Makefile provides a consistent interface across PactFlow's 30+ example repositories. If you're unfamiliar with Make, use the native commands shown in the Usage section below.

What is Bi-Directional Contract Testing?

Bi-Directional Contract Testing is a contract testing approach that allows you to use your existing mock server (like MSW) and API specifications (like OpenAPI) to verify that services can work together.

How it works:

  1. Consumer Side (this project): Your tests run against MSW mocks, generating a Pact contract as output that describes what the consumer expects from the provider. This contract is then uploaded to PactFlow.
  2. Provider Side: The provider has an OpenAPI Specification (OAS) that may be input to their tests. The combination of the test results validating the OAS, plus the OAS itself, forms the provider contract, which is then uploaded to PactFlow.
  3. PactFlow: Compares the consumer's expectations (Pact) against the provider's specification (OAS) to ensure compatibility

Key Benefits:

  • Use Your Existing Tools: Keep using MSW for mocking - no need to learn Pact-specific mocking
  • Automated Compatibility Checks: PactFlow automatically validates that your consumer expectations match the provider's API before deployment
  • Fast Feedback: Know immediately if a provider change will break your consumer, or if your consumer expects something the provider doesn't offer
  • Safe Deployments: The can-i-deploy check ensures you only deploy compatible versions

Unlike traditional Pact (which requires both sides to use Pact), bi-directional testing lets each side use their preferred tools while still maintaining contract safety.

Key points with Mock Service Worker

This example:

  • Implements a React-based "Product" website built with Vite and TypeScript
  • Utilizes MSW to mock out the Product API provider
  • Utilizes pact-msw-adapter to transform MSW mocks into Pact consumer contracts

Tech Stack

This project uses the following web development tools:

  • TypeScript - Full type safety with ESNext target
  • Vite - Fast build tool and dev server
  • Vitest - Modern test runner with native ESM support
  • Biome - Fast formatter and linter with TypeScript and React support
  • React Router v7 - Latest routing library
  • MSW v2 - API mocking for browser and Node.js

Overview of Part of Bi-Directional Contract Testing Flow

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.

graph LR
    Consumer[Consumer]
    Mock[Mock<br/>e.g. Pact]
    PactFlow[PactFlow performs check<br/>to confirm that the Pact<br/>contract is compatible with<br/>Provider OAS]
    Contract[Consumer<br/>Contract]
    Provider[Provider]
    ProviderTest[Provider Testing<br/>Tool BYO]
    Validation{Cross Contract<br/>Validation}

    Consumer -->|1. Run tests| Mock
    Mock -->|2. Generates| Contract
    Contract -->|3. Upload| PactFlow
    Provider -->|Provider<br/>Contract| ProviderTest
    ProviderTest -->|OAS| PactFlow
    PactFlow -->|4. Validates| Validation

    style Consumer fill:#9f6ce6
    style Provider fill:#9f6ce6
    style PactFlow fill:#1eb5a0
    style Validation fill:#5cb85c
Loading

Note

The provider contract consists of both the OpenAPI Specification (OAS) and the test results that validate it. The OAS may be used as input to the provider's tests, and the validated OAS is what gets uploaded to PactFlow.

The project uses a Makefile to simulate a very simple build pipeline with two stages - test and deploy. See the table above for native command equivalents, or the detailed Usage section below for native npm and Docker commands.

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 (i.e., has the cross-contract validation been successfully performed)
  • Deploy (only from master)
    • Deploy app to Production
    • Record the Production deployment in the Pact Broker
graph TB
    subgraph CP["Consumer Pipeline"]
        RunTests[Run tests]
        Publish[Publish contract]
        CanDeploy[can-i-deploy]
        Deploy[Deploy!]

        RunTests -->|Generate contract| Publish
        Publish --> CanDeploy
        CanDeploy -->|Approved| Deploy
    end

    subgraph PF["PactFlow Server"]
        Broker[(Contract Broker)]
        Validation[Cross-Contract<br/>Validation]
        ProviderOAS[Provider OAS<br/>in Production]

        Broker -->|Compare| Validation
        ProviderOAS -->|Against| Validation
    end

    Publish -.->|Upload contract| Broker
    CanDeploy <-.->|Request validation| Validation

    style RunTests fill:#9f6ce6
    style Publish fill:#9f6ce6
    style CanDeploy fill:#9f6ce6
    style Deploy fill:#9f6ce6
    style Validation fill:#5cb85c
    style CP fill:#2d2d2d,stroke:#9f6ce6,stroke-width:3px
    style PF fill:#2d2d2d,stroke:#1eb5a0,stroke-width:3px
Loading

Note

The cross-contract validation happens on PactFlow's server when can-i-deploy is called. It compares the consumer contract against the provider's OAS that's already in production to ensure compatibility before deployment.

Compatible with Providers

This project is currently compatible with the following provider(s):

See Environment variables on how to set these up.

Pre-requisites

Software:

Environment variables

To be able to run some of the commands locally, you will need to export the following environment variables into your shell:

Set PACT_PROVIDER to one of the following:

Usage

Running the Application

Start the development server to see the Product website:

npm install
npm run dev

The app will open at http://localhost:3000 and display a list of products.

Running Tests

Run the test suite with Vitest:

npm test

This runs all tests, including the MSW-based API tests that generate the Pact contract which will be located in the pacts/ directory.

Development Commands

Useful commands for development:

# Type checking
npm run type-check

# Check linting and formatting
npm run check
# Variant to auto-fix issues
npm run check:fix

# Production build
npm run build

# Preview production build
npm run preview

# Emulate the full CI pipeline locally (requires Docker, PACT_BROKER_BASE_URL, PACT_BROKER_TOKEN)
make fake_ci

Use case with pact-msw-adapter

Check out the pact-msw-adapter repo here for more details in the readme.

Key implementation files:

OS/platform-specific considerations

The Makefile is configured to run on Unix-based systems such as you would find in most common CI/CD pipelines.

Commands can be run locally on Unix/Mac, or on Windows via WSL2.

Windows

You can still try this example locally on Windows using PowerShell and running commands manually.

Click to see Windows-specific instructions here

These will be the same commands that are used in the Makefile with a few manual tweaks.

  1. Make sure you have set all of the environment variables. In PowerShell, they can be set like so:

     $env:GIT_BRANCH="main"
    
  2. Publish the pact that was generated. The step uses the pact-cli docker image to publish the pact to your pactflow account. The path for <path_to_project_root> needs to be converted from Windows paths to UNIX ones as the Docker container is using UNIX. Either hard code this or set it as another environment variable.

    C:\Users\Person\Documents\example-bi-directional-consumer-msw

    becomes

    /c/Users/Person/Documents/example-bi-directional-consumer-msw

    $env:VARIABLE_NAME refers to environment variables in Windows.

    docker run --rm -v <path_to_project_root>:<path_to_project_root> -e PACT_BROKER_BASE_URL -e PACT_BROKER_TOKEN pactfoundation/pact-cli publish <path_to_pacts_folder> --consumer-app-version $env:GIT_COMMIT --branch $env:GIT_BRANCH
    
    
  3. Check can-i-deploy to see if your provider is compatible with your pact.

    docker run --rm -v <path_to_project_root>:<path_to_project_root> -e PACT_BROKER_BASE_URL -e PACT_BROKER_TOKEN pactfoundation/pact-cli  broker can-i-deploy --pacticipant pactflow-example-bi-directional-consumer-msw --version $env:GIT_COMMIT --to-environment production  --retry-while-unknown 0 --retry-interval 10
    
  4. Have a look at what other commands are available in the Makefile. All of them can be run locally from PowerShell by changing the Windows paths to UNIX format and replacing the environment variable references. Any variable referenced as ${VARIABLE} can be changed to $env:VARIABLE to reference environment variables in PowerShell.

Caveats

Related topics / posts / discussions

Found an issue?

Reach out via a GitHub Issue, or reach us over in the Pact foundation Slack

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors