Consumer Helpers
Convert typed objects to Pact-compatible JsonMap params and configure request/response JSON callbacks with createProviderState, toJsonMap, and setJsonContent.
Opinionated guardrails for enterprise-scale contract testing.
Pact.js gives you primitives and lets each team wire them together. That flexibility becomes a liability at scale: team A configures selectors one way, team B forgets the breaking-change flag, team C hard-codes broker URLs.
This library enforces a single pattern across every repository:
| Problem with raw Pact | What goes wrong | pactjs-utils |
|---|---|---|
| Each team wires their own setup | Style drifts across repos; 30-line boilerplate diverges silently. | buildVerifierOptions / buildMessageVerifierOptions — one call, same config shape, every repo. |
| DIY consumer version selectors | Team A verifies mainBranch, team B doesn't. Deployments break silently. | includeMainAndDeployed: true/false — one boolean, same selectors everywhere. |
| Manual broker URL / webhook handling | Hardcoded URLs break when the broker moves; HTTP suite consumes a Kafka webhook URL (or vice versa). | handlePactBrokerUrlAndSelectors reads env vars, routes URLs, ignores cross-execution mismatches. |
| No breaking-change workflow | Provider tagged as dev while consumer hasn't caught up — broken prod. | When your test maps PACT_BREAKING_CHANGE to includeMainAndDeployed: false, selectors narrow to matchingBranch and dev is dropped from provider version tags. |
| Auth injection left to the developer | Double-Bearer prefix (Bearer Bearer <token>) — 401 on every run. | createRequestFilter adds Bearer exactly once; tokenGenerator returns the raw value. |
Manual JsonMap casting | [object Object] in provider state params — handler crashes. | toJsonMap converts deterministically; createProviderState wraps the pattern. |
| Repetitive builder lambdas | Every interaction repeats builder.query(...) / builder.jsonBody(...). | setJsonContent — one curried helper reusable across request and response builders. |
// pact/http/consumer/movies-read.pacttest.ts
import {
createProviderState,
setJsonContent
} from '@seontechnologies/pactjs-utils'
const state = createProviderState({
name: 'Has a movie with a specific ID',
params: { id: 1, name: 'Inception', year: 2010 }
})
await pact
.addInteraction()
.given(...state)
.withRequest(
'GET',
'/movies',
setJsonContent({
query: { name: 'Inception' }
})
)
.willRespondWith(
200,
setJsonContent({
body: {
status: 200,
data: [{ id: 1, name: 'Inception' }]
}
})
)// pact/http/provider/provider-contract.pacttest.ts
import { buildVerifierOptions } from '@seontechnologies/pactjs-utils'
const options = buildVerifierOptions({
provider: 'SampleMoviesAPI',
port: '3001',
stateHandlers,
includeMainAndDeployed: process.env.PACT_BREAKING_CHANGE !== 'true',
requestFilter: createRequestFilter({
tokenGenerator: () => generateAuthToken({ userIdentifier: 'admin' })
})
})
const verifier = new Verifier(options)
await verifier.verifyProvider()import {
createRequestFilter,
noOpRequestFilter
} from '@seontechnologies/pactjs-utils'
// Custom token generator
const filter = createRequestFilter({
tokenGenerator: () =>
`${new Date().toISOString()}:${JSON.stringify({ userId: 'admin' })}`
})
// Default (ISO timestamp token)
const defaultFilter = createRequestFilter()// pact/message/provider/provider-message-queue.pacttest.ts
import { buildMessageVerifierOptions } from '@seontechnologies/pactjs-utils'
const options = buildMessageVerifierOptions({
provider: 'SampleMoviesAPI-event-producer',
messageProviders,
stateHandlers,
includeMainAndDeployed: true
})
const messagePact = new MessageProviderPact(options)
await messagePact.verify()| Category | Utilities | Docs |
|---|---|---|
| Consumer | toJsonMap, createProviderState, setJsonContent, setJsonBody | Consumer Helpers |
| Provider | buildVerifierOptions, buildMessageVerifierOptions, handlePactBrokerUrlAndSelectors, getProviderVersionTags | Provider Verifier |
| Auth | createRequestFilter, noOpRequestFilter | Request Filter |
| Type | Status | Cost | Local | Directory |
|---|---|---|---|---|
| Consumer-Driven (HTTP) | Implemented | Free | Yes | pact/http/ |
| Message Queue (Kafka/async) | Implemented | Free | Yes | pact/message/ |
| Bi-Directional (provider-driven) | Implemented | PactFlow req | No | scripts/ (OAS) |
Read more about each type and the testing flows in Concepts.
Each workflow is a standalone file that can be copied to its own repo.
| Workflow | Repo | Trigger | Purpose |
|---|---|---|---|
contract-test-consumer.yml | Consumer | PR + push to main | Generate pacts, publish, can-i-deploy |
contract-test-provider.yml | Provider | PR + push to main | Verify HTTP pacts, can-i-deploy |
contract-test-provider-message.yml | Provider | PR + push to main | Verify message pacts, can-i-deploy |
contract-test-webhook.yml | Provider | PactFlow webhook | Verify when consumer publishes new pact |
contract-test-publish-openapi.yml | Provider | Push to main + manual | Publish OAS to PactFlow (BDCT) |
contract-test-local.yml | Monorepo | PR + manual | Local consumer + provider (no broker) |