nestjs-local-events
v0.0.1
Published
Dev-only helper for dispatching events directly to local consumers in NestJS (bypass Kafka/Rabbit during local dev).
Downloads
6
Maintainers
Readme
nestjs-local-events
Lightweight dev-only helper for NestJS. During local development, it delivers events directly to in-process consumer methods, bypassing external brokers. In production it is a no-op.
Why
Debugging is easier when producers and consumers run in the same process locally. Mark the producer method with a decorator and mark the consumer with a decorator; when NODE_ENV !== 'production', emitted events are routed instantly to the matching consumers.
Installation
npm i -D nestjs-local-eventsimport { Module } from '@nestjs/common';
import { LocalEventsModule } from 'nestjs-local-events';
@Module({
imports: [
...(process.env.NODE_ENV !== 'production' ? [LocalEventsModule.forRoot()] : []),
],
})
export class AppModule {}End-to-end Example
This is a complete, minimal flow for a single case: an Order domain.
// events.ts
export enum OrderEvents {
OrderPlaced = 'order.placed',
PaymentCaptured = 'payment.captured',
}// order.consumer.ts
import { Injectable } from '@nestjs/common';
import { LocalConsumer } from 'nestjs-local-events';
import { OrderEvents } from './events';
@Injectable()
export class OrderConsumer {
public seen: Array<{ type: OrderEvents; payload: unknown }> = [];
@LocalConsumer(OrderEvents.OrderPlaced)
async onOrderPlaced(payload: { orderId: string; userId: string }) {
this.seen.push({ type: OrderEvents.OrderPlaced, payload });
}
}// order.service.ts
import { Injectable } from '@nestjs/common';
import { Local as RouteLocal, LocalEventBus, LocalEventMessage } from 'nestjs-local-events';
import { OrderEvents } from './events';
@Injectable()
export class OrderService {
constructor(public readonly localEventBus: LocalEventBus) {}
@RouteLocal()
async place(dto: { userId: string }): Promise<LocalEventMessage<{ orderId: string; userId: string }>> {
const orderId = 'o-1';
return { type: OrderEvents.OrderPlaced, payload: { orderId, userId: dto.userId } };
}
@RouteLocal({
extractor: (_args, result: { id: string; amount: number }) => ({
type: OrderEvents.PaymentCaptured,
payload: { orderId: result.id, amount: result.amount },
}),
})
async capture(orderId: string) {
return { id: orderId, amount: 5000 };
}
}Behavior
- When
NODE_ENV !== 'production', methods decorated withLocal(aliased asRouteLocalabove) extract an event from the method result and dispatch it to all@LocalConsumer(event)handlers in the same process. - When
NODE_ENV === 'production', the decorator does nothing and simply returns the original method result.
Requirements
- The producer class must inject
LocalEventBusas a public field:constructor(public readonly localEventBus: LocalEventBus) {}. - You may return a
LocalEventMessagefrom the method, or provide anextractorto convert arbitrary results into aLocalEventMessage.
API
type EventEnum = string | number
interface LocalEventMessage<Payload = unknown> {
type: EventEnum
payload: Payload
}
type ExtractorFn = (args: unknown[], result: unknown) => LocalEventMessage | LocalEventMessage[] | null | undefined
interface LocalOptions {
extractor?: ExtractorFn
enabled?: boolean
}
declare function Local(event?: LocalOptions): MethodDecorator
declare function LocalConsumer(event: EventEnum): MethodDecoratorPublishing
- CI runs build and tests on every push and PR.
- Creating a tag
vX.Y.Ztriggers the release workflow. - The workflow enforces the package.json version to match the tag and publishes to npm using
NPM_TOKEN.
