Use this guide to wire CAP into a NestJS application.
The in-memory bundle is the fastest way to try CAP in tests or local examples. It keeps outbox/inbox records in memory and uses an in-process bus.
import { Module } from '@nestjs/common';
import { CapModule } from '@mikara89/cap-nest';
@Module({
imports: [CapModule.forInMemory()],
})
export class AppModule {}
Publish from any injectable that receives CapService:
import { Injectable } from '@nestjs/common';
import { CapService } from '@mikara89/cap-nest';
@Injectable()
export class UsersService {
constructor(private readonly cap: CapService) {}
async createUser(): Promise<void> {
await this.cap.publish('user.created', {
id: 'u1',
email: 'alice@example.com',
});
}
}
Handle messages with @CapSubscribe:
import { Injectable } from '@nestjs/common';
import {
CapHeaders as CapHeadersParam,
CapSubscribe,
type CapHeaders,
} from '@mikara89/cap-nest';
@Injectable()
export class MailHandler {
@CapSubscribe({ topic: 'user.created', group: 'mail-service' })
async handleUserCreated(
payload: { id: string; email: string },
@CapHeadersParam() headers?: CapHeaders,
) {
// send welcome email
}
}
Production apps should provide durable storage and an external transport. First-party adapters include MikroORM, Knex, TypeORM, and Prisma storage, and Azure Service Bus, NestJS microservices, RabbitMQ, Kafka, and AWS SNS/SQS transport. See Adapters for the full list and setup details.
Warning: multi-instance durable outbox dispatch requires a lock-capable MikroORM SQL driver such as PostgreSQL or MySQL, or a custom storage adapter with equivalent claim safety. SQLite and other local/non-locking drivers are supported only for demos, development, and single-process tests. SQL Server requires a future SQL Server-specific claim implementation before it is supported for multi-instance dispatch by the first-party MikroORM adapter.
import { Module } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { CapModule } from '@mikara89/cap-nest';
import {
CapPublishEntity,
CapReceivedEntity,
} from '@mikara89/cap-storage-mikro-orm';
import { MikroStorageModule } from '@mikara89/cap-storage-mikro-orm/nest';
import { ServiceBusTransportModule } from '@mikara89/cap-transport-azure-servicebus/nest';
const serviceBusTransport = ServiceBusTransportModule.forRoot({
connectionString: process.env.AZURE_SERVICEBUS_CONNECTION_STRING!,
topicPrefix: 'cap-',
subscriptionPrefix: 'sub-',
});
@Module({
imports: [
MikroOrmModule.forRoot({
dbName: process.env.DB_NAME,
entities: [CapPublishEntity, CapReceivedEntity],
}),
MikroStorageModule,
serviceBusTransport,
CapModule.forRoot({
imports: [MikroStorageModule, serviceBusTransport],
init: {
createSchema: false,
createQueues: false,
},
}),
],
})
export class AppModule {}
Use environment variables for secrets. Do not commit Service Bus connection strings or database credentials.
The dashboard is optional and must be protected by a guard.
import { CapDashboardModule } from '@mikara89/cap-dashboard-nest';
@Module({
imports: [
CapDashboardModule.forRoot({
guard: {
provide: 'CAP_DASHBOARD_GUARD',
useValue: { canActivate: () => true },
},
authorizer: {
provide: 'CAP_DASHBOARD_AUTHORIZER',
useValue: ({ permission }) => permission === 'read',
},
routePrefix: '/api/cap',
uiRoute: '/cap-dashboard',
}),
],
})
export class AppModule {}
Replace the sample guard and authorizer with real application policy before exposing the dashboard outside local development.