@nestjs-event-driven/core
v0.2.0
Published
This package was inspired by [NestJS CQRS](https://github.com/nestjs/cqrs).
Downloads
3
Readme
Nest Event Driven Core
This package was inspired by NestJS CQRS.
Important Update: As of the latest version, this package is now based on @event-driven-architecture/core. This dependency provides the core functionality while this package adds NestJS-specific integration.
The main purpose of this package is to provide a core functionality for building event driven architecture in NestJS.
EventBus was extended with additional methods to make it possible to extend event routing for specific integrations and enable acknowledgement mechanism for message brokers.
Disclaimer
This package is still under development and the API may change in further releases. Documentation may not cover all features.
Installation
First, let's install the package using your preferred package manager:
# Using npm
npm install @nestjs-event-driven/core
# Using yarn
yarn add @nestjs-event-driven/core
# Using pnpm
pnpm add @nestjs-event-driven/core
# Using bun
bun add @nestjs-event-driven/coreModule Registration
To start using event-driven architecture in your NestJS application, import the EventDrivenModule into your root module:
import { EventDrivenModule } from '@nestjs-event-driven/core';
import { Module } from '@nestjs/common';
@Module({
imports: [
EventDrivenModule.forRoot(),
// other modules...
],
})
export class AppModule {}You can also use the register method for non-global registration:
import { EventDrivenModule } from '@nestjs-event-driven/core';
import { Module } from '@nestjs/common';
@Module({
imports: [
EventDrivenModule.register(),
// other modules...
],
})
export class FeatureModule {}Event Handlers
Creating Events
First, define your events by implementing the IEvent interface:
import { IEvent } from '@nestjs-event-driven/core';
interface IUserCreatedEventPayload {
userId: string;
}
export class UserCreatedEvent implements IEvent<IUserCreatedEventPayload> {
constructor(public readonly payload: IUserCreatedEventPayload) {}
}Creating Event Handlers
Next, create handlers for your events using the @EventHandler decorator:
import { EventHandler, IEventHandler } from '@nestjs-event-driven/core';
import { Injectable } from '@nestjs/common';
import { UserCreatedEvent } from './events/user-created.event';
@Injectable()
@EventHandler({ event: UserCreatedEvent })
export class UserCreatedEventHandler implements IEventHandler<UserCreatedEvent> {
handle(event: UserCreatedEvent): void {
const { userId } = event.payload;
// Handle the event
console.log(`User created with ID: ${userId}`);
}
}Handler Registration
Event handlers are automatically registered during the application bootstrap process. The EventDrivenModule uses NestJS's lifecycle hooks to discover and register all event handlers.
Note: Handlers are registered during the
onApplicationBootstraplifecycle hook. For more information about NestJS lifecycle hooks, see the official documentation.
Event Bus
Publishing Events
To publish events, inject the EventBus into your service:
import { EventBus } from '@nestjs-event-driven/core';
import { Injectable } from '@nestjs/common';
import { UserCreatedEvent } from './events/user-created.event';
@Injectable()
export class UserService {
constructor(private readonly eventBus: EventBus) {}
createUser(userId: string): void {
// Business logic...
// Publish event
this.eventBus.publish(new UserCreatedEvent({ userId }));
}
}Setting Up a Publisher
To set up a publisher that will emit events to a message broker or your personal event flow, you can use one of the provided approaches:
Option 1: Using Module Options
The recommended approach is to provide the publisher using the eventPublisher option when configuring the module:
import { EventDrivenModule, IEventPublisher } from '@nestjs-event-driven/core';
import { Module } from '@nestjs/common';
@Injectable()
class MyCustomPublisher implements IEventPublisher {
// ... your implementation
}
@Module({
imports: [
EventDrivenModule.forRoot({
eventPublisher: MyCustomPublisher,
}),
],
providers: [MyCustomPublisher],
})
export class AppModule {}You can also use forRootAsync for dynamic configuration:
import { EventDrivenModule } from '@nestjs-event-driven/core';
import { Module } from '@nestjs/common';
@Module({
imports: [
EventDrivenModule.forRootAsync({
useFactory: () => ({
eventPublisher: MyCustomPublisher,
}),
}),
],
providers: [MyCustomPublisher],
})
export class AppModule {}Option 2: Declarative way
Alternatively, you can use the @GlobalEventPublisher decorator when you have registered EventDrivenModule globally:
import { EventBus, GlobalEventPublisher, IEventPublisher } from '@nestjs-event-driven/core';
import { Module } from '@nestjs/common';
@GlobalEventPublisher()
class MyCustomPublisher implements IEventPublisher {
// ... your implementation
}
@Module({
imports: [EventDrivenModule.forRoot()],
providers: [MyCustomPublisher],
})
export class AppModule {}Note: When both approaches are used, the
eventPublisheroption takes precedence over the@GlobalEventPublisherdecorator. Only one provider across the application should be annotated with@GlobalEventPublisher.
Note: When using the
eventPublisheroption, provide the class token (not an instance) of your publisher implementation. The module will resolve and instantiate it automatically.
Consuming Events Synchronously
The EventBus provides methods for consuming events synchronously:
// Consume by a single handler (throws if multiple handlers exist)
await eventBus.synchronouslyConsumeByStrictlySingleHandler(new UserCreatedEvent({ userId: '123' }));
// Consume by multiple handlers
await eventBus.synchronouslyConsumeByMultipleHandlers(new UserCreatedEvent({ userId: '123' }));Core Definitions
The event-driven module provides several key definitions:
Event (IEvent) - Base interface for all events. Events are simple data structures that contain information about what happened in your application.
Event Handler (IEventHandler) - Interface for event handlers. Handlers contain the business logic that should be executed when a specific event occurs.
Event Bus (IEventBus) - Core interface for the event bus. The event bus is responsible for publishing events and routing them to the appropriate handlers.
Event Publisher (IEventPublisher) - Interface for publishing events to external systems. Publishers are responsible for sending events to external message brokers or other systems.
Event Subscriber (IEventSubscriber) - Interface for subscribing to events. Used for in-app synchronous events, similar to an event emitter.
Handler Register (IHandlerRegister) - Interface for the handler register service. Responsible for registering handlers and retrieving handler signatures.
Scoped Handlers with Context
You can create scoped handlers that receive context information:
import { EventHandlerScope } from '@nest-event-driven/core';
import { EventHandler, IEventHandler } from '@nestjs-event-driven/core';
import { Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { UserCreatedEvent } from './events/user-created.event';
interface IEventContext {
requestId: string;
}
@Injectable()
@EventHandler({ event: UserCreatedEvent }, { scope: EventHandlerScope.SCOPED })
export class ScopedUserCreatedEventHandler implements IEventHandler<UserCreatedEvent> {
constructor(@Inject(REQUEST) private readonly context: IEventContext) {
// Access request context
console.log('Request context:', context);
}
handle(event: UserCreatedEvent): void {
// Handle the event with access to request context
// ...
}
}When consuming events, you can pass context:
await eventBus.synchronouslyConsumeByStrictlySingleHandler(new UserCreatedEvent({ userId: '123' }), {
context: { requestId: '456' },
});