README.md
June 17, 2026 ยท View on GitHub
NestJS + Pact
Injectable Pact.js Consumer/Provider for NestJS
Table Of Contents
Installation
Pact-JS v10.x+ users
npm i -D nestjs-pact @pact-foundation/pact
Pact-JS v9.x users
npm i -D nestjs-pact@2.2.2 @pact-foundation/pact@9.18.1
Example
If you want to see a fully working end-to-end example with NestJS and Pact I recommend you to jump to the NestJS official examples at the PactJS Github repository
About
This package enables you to consume Pact.js in a way that can be used very easily in NestJS.
Like the nature of Pact, this package is for testing purposes only.
If you are not familiar with Pact, Pact is fast, easy and reliable testing framework for integrating web apps, APIs and microservices. Read more on Pact official website
There are two main modules suggested; one for the Provider role (Verifier), and one for the Consumer role (creating Pact files), each loaded separately.
Of course, you can also use both modules and play the role of Consumer and Provider at the same time.
Introduction
The use of each of the modules suggested here, is made in the common and accepted form of NestJS modules.
The simplest way is to use the register method and pass the settings directly.
It is also enable to use the registerAsync method to pass the settings in the form of useFactory or useClass for example.
One more thing - the usage of the modules is for testing purposes only, which is not quite common in the use of NestJS modules, so there are some good examples down below. The obvious advantage of this package is that Pact can be used in combination with the techniques and benefits offered by NestJS.
Consumer
In order to use the Consumer module, you need to follow a few simple steps, let's go over it!
First, create a file called pact.module.ts in your test folder (or wherever you put your tests), and simply
load the PactConsumerModule, PactV2ConsumerModule, or PactV3ConsumerModule like below:
test/pact/pact.module.ts
V2 setup
import { Module } from '@nestjs/common';
import { PactV2ConsumerModule } from 'nestjs-pact';
import { PactV2ConsumerConfigOptionsService } from './pact-consumer-config-options.service';
import { AppModule } from '../../src/app.module';
@Module({
imports: [
PactV2ConsumerModule.registerAsync({
imports: [AppModule],
useClass: PactV2ConsumerConfigOptionsService,
}),
],
})
export class PactModule {}
V3 setup
import { DynamicModule, Module } from '@nestjs/common';
import { PactV3ConsumerModule } from 'nestjs-pact';
import { PactV3ConsumerConfigOptionsService } from './pact-consumer-config-options.service';
import { AppModule } from '../../src/app.module';
@Module({
imports: [
PactV3ConsumerModule.registerAsync({
imports: [AppModule],
useClass: PactV3ConsumerConfigOptionsService,
}) as DynamicModule,
],
})
export class PactModule {}
V4 setup
import { DynamicModule, Module } from '@nestjs/common';
import { PactConsumerModule } from 'nestjs-pact';
import { PactConsumerConfigOptionsService } from './pact-consumer-config-options.service';
import { AppModule } from '../../src/app.module';
@Module({
imports: [
PactConsumerModule.registerAsync({
imports: [AppModule],
useClass: PactConsumerConfigOptionsService,
}) as DynamicModule,
],
})
export class PactModule {}
Yay, now let's create the test file! let's call it my-test.spec.ts
test/pact/my-test.spec.ts
V2 test
import { PactV2 } from '@pact-foundation/pact';
import { Test } from '@nestjs/testing';
import { PactV2Factory } from 'nestjs-pact';
import { PactModule } from '@test/pact/pact.module';
describe('Pact', () => {
let pactFactory: PactV2Factory;
let provider: PactV2;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [SomeOtherModule, AndAnotherModuleYouNeed, PactModule],
}).compile();
pactFactory = moduleRef.get(PactV2Factory);
provider = pactFactory.createContractBetween({
consumer: 'Consumer Service Name',
provider: 'Provider Service Name',
});
await provider.setup();
});
afterEach(() => provider.verify());
afterAll(() => provider.finalize());
describe('when something happens', () => {
describe('and another thing happens too', () => {
beforeAll(() => provider.addInteraction({ ... }));
it('should do something', () => {
return expect( ... );
});
});
});
});
V3 test
import { PactV3 } from '@pact-foundation/pact';
import { Test } from '@nestjs/testing';
import { PactV3Factory } from 'nestjs-pact';
import { PactModule } from '@test/pact/pact.module';
describe('Pact', () => {
let pactFactory: PactV3Factory;
let provider: PactV3;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [SomeOtherModule, AndAnotherModuleYouNeed, PactModule],
}).compile();
pactFactory = moduleRef.get(PactV3Factory);
provider = pactFactory.createContractBetween({
consumer: 'Consumer Service Name',
provider: 'Provider Service Name',
});
});
describe('when something happens', () => {
describe('and another thing happens too', () => {
beforeAll(() => provider.addInteraction({ ... }));
it('returns a 401 unauthorized', () => {
return provider.executeTest(async (mockServer) => {
// mockServer.url is the full url + port of the pact mock server
process.env.API_HOST = mockServer.url;
return expect( ... );
});
});
});
});
});
V4 test
import { Pact } from '@pact-foundation/pact';
import { Test } from '@nestjs/testing';
import { PactFactory } from 'nestjs-pact';
import { PactModule } from '@test/pact/pact.module';
describe('Pact', () => {
let pactFactory: PactFactory;
let provider: Pact;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [SomeOtherModule, AndAnotherModuleYouNeed, PactModule],
}).compile();
pactFactory = moduleRef.get(PactFactory);
provider = pactFactory.createContractBetween({
consumer: 'Consumer Service Name',
provider: 'Provider Service Name',
});
});
describe('when something happens', () => {
it('returns the animal', async () => {
await provider
.addInteraction()
.given('Has an animal with ID 1')
.uponReceiving('a request for an animal with ID 1')
.withRequest('GET', regex('/animals/[0-9]+','/animals/1'), (builder) => {
builder.headers({ Authorization: 'Bearer token' });
})
.willRespondWith(HttpStatus.OK, (builder) => {
builder.headers({ 'Content-Type': 'application/json; charset=utf-8' });
builder.jsonBody(animalBodyExpectation);
})
.executeTest(async (mockServer) => {
process.env.API_HOST = mockServer.url;
const suggestedMates = await animalsService.getAnimalById(11);
expect(suggestedMates).toHaveProperty('id', 1);
});
});
});
});
Publishing Pacts
Publishing pacts to a Pact Broker is done outside of this library using the @pact-foundation/pact-cli npm package, which wraps the Pact CLI binaries.
Install the CLI as a dev dependency:
npm i -D @pact-foundation/pact-cli
Then publish your pacts from your CI pipeline:
npx pact-broker publish ./pacts \
--broker-base-url=https://your-broker.example.com \
--consumer-app-version=$(git rev-parse HEAD) \
--branch=$(git rev-parse --abbrev-ref HEAD)
Or add it as a package.json script:
{
"scripts": {
"pact:publish": "pact-broker publish ./pacts --broker-base-url=$PACT_BROKER_URL --consumer-app-version=$GIT_COMMIT --branch=$GIT_BRANCH"
}
}
Publishing is typically only done in CI โ not locally. See the Pact CLI docs for all available options.
Provider
The usage in the Provider service is quite easy; In your /test folder (or wherever you put your tests)
create a simple test module with NestJS Test.createTestingModule method and import the PactProviderModule module
from nestjs-pact.
You can use register or registerAsync method, make sure you stick to PactProviderOptions interface options.
After creating the Nest application from the testing module, pass the app instance to the verify method,
it will generate a random (available) port, spin up the application and run the verifier against the application url.
You can read more about Pact Verification in the official Pact documentation
Here is a quick and simple example:
import { Test } from '@nestjs/testing';
import { INestApplication, Logger, LoggerService } from '@nestjs/common';
import { PactProviderModule, PactVerifierService } from 'nestjs-pact';
import { AppModule } from '@app/app.module';
describe('Pact Verification', () => {
let verifierService: PactVerifierService;
let logger: LoggerService;
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule, PactProviderModule.register({ ... })],
}).compile();
verifierService = moduleRef.get(PactVerifierService);
logger = moduleRef.get(Logger);
app = moduleRef.createNestApplication();
await app.init();
});
it('validates the expectations of Matching Service', async () => {
const { output } = await verifierService.verify(app);
logger.log('Pact Verification Completed!');
logger.log(output);
});
afterAll(async () => {
await app.close();
});
});
License
Distributed under the MIT License. See LICENSE for more information.