Verify Provider Contracts with Consumer-Driven Pact
Publish consumer expectations as pacts, verify the provider against them in CI, and break the build on any contract drift before deploy.
Task
Set up consumer-driven contract tests so a provider change that breaks any consumer fails CI instead of production.
When to use this
The API team renames a field. Their tests pass, three consumers break silently, and the discovery happens in production logs over two days.
Prerequisites
- A Pact broker or another shared pact exchange
- One consumer with tests expressing its expectations
- CI access on the provider side to run verification
Steps
1. Write the consumer test that states its needs.
describe("GET /orders/{id}", () => {
it("returns the fields the order list uses", () => {
return provider.mockResponse({
id: 1,
totalCents: 4200,
status: "shipped",
});
});
});
Expected output: The consumer test documents exactly the fields it depends on, no more, no less.
2. Publish the pact from consumer CI.
npx @pact-foundation/cli pact-broker publish pacts/ --consumer-app-version $GIT_SHA --tag mainExpected output: The pact is visible in the broker, tagged to the consumer version that produced it.
3. Verify the provider against every published pact.
const { Verifier } = require("@pact-foundation/pact");
new Verifier({
providerBaseUrl: "http://localhost:3000",
pactBrokerUrl: process.env.PACT_BROKER_URL,
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
}).verifyProvider();
Expected output: The provider CI replays every consumer expectation against the real API and reports results to the broker.
4. Fail the provider build on broken contracts.
const success = await verifier.verifyProvider();
if (!success) {
console.error("provider broke a consumer contract");
process.exit(1);
}
Expected output: The field rename now fails provider CI the moment it lands, naming the consumer it would break.
5. Check can-i-deploy before releasing either side.
npx @pact-foundation/cli can-i-deploy --pacticipant orders-api --version $GIT_SHA --to-environment productionExpected output: The broker answers whether this provider version is verified against every consumer currently in production.
Troubleshooting
Problem
Provider verification fails on test data mismatch
Solution
The provider fixture differs from the pact expectation; align state setup with provider states declared in the pact, and keep fixtures minimal.
Problem
Too many pacts slow provider CI
Solution
Filter verification to changed consumers via the broker webhook mode, or run full verification nightly and on-demand for releases.
Problem
Consumers do not write pacts
Solution
Start with the one consumer that broke last; contract testing spreads by demonstrated incident prevention, not mandate.
Limitations
- Pact verifies request-response shape and status semantics, not performance or business rules.
- The broker becomes shared infrastructure; someone must own its availability and retention policy.
The consumer defines the contract
Provider-side API tests prove the provider agrees with itself, which is not the contract that matters. Consumer-driven contracts invert the authority: each consumer publishes the exact fields it needs, and the provider must satisfy every published expectation. The rename that passes provider tests then fails verification against the consumer pact, in CI, with the affected team named in the failure.
new Verifier({
providerBaseUrl: "http://localhost:3000",
pactBrokerUrl: process.env.PACT_BROKER_URL,
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
}).verifyProvider();
can-i-deploy is the release gate
Verification records which provider versions satisfy which consumer pacts. The can-i-deploy query turns that history into a deploy decision: this exact version is safe for production only if verified against every consumer already there. It replaces the folklore of we think all clients upgraded with a broker-recorded fact.
Start with the last incident
Adoption stalls when sold as a platform initiative. Start with the integration that broke most recently: one consumer, one pact, one verification job. The first time a provider PR fails with the name of the consumer it would have broken, the next team volunteers itself.