nestjs / event-emitter Goto Github PK
View Code? Open in Web Editor NEWEvent Emitter module for Nest framework (node.js) 🦋
Home Page: https://nestjs.com
License: MIT License
Event Emitter module for Nest framework (node.js) 🦋
Home Page: https://nestjs.com
License: MIT License
@OnEvent(TicketEventEnum.UPDATE, { async: true })
async test() {
console.error('test');
}
if (promisify === undefined) {
promisify = listener.constructor.name === 'AsyncFunction';
}
promisify is false。listener is ticket.update (...args) => instance[methodKey].call(instance, ...args)
the .constructor.name === 'Function';
https://github.com/nestjs/event-emitter/blob/master/lib/event-subscribers.loader.ts#L95
this can cause emitAsync.catch not working !
https://github.com/nestjs/event-emitter/blob/master/lib/event-subscribers.loader.ts#L95
No response
listener constructor name is AsyncFunction
9.4
9.4
16
No response
Please add a License to the repository. The current URL under Readme
http://github.com/nestjs/event-emitter/blob/master/LICENSE resolves to a 404.
Thanks
Currently any event listeners (ie @OnEvent("MY_EVENT")
) default to suppressing exceptions. The default logger message when suppressing exceptions only prints the error message and ignores the stack if it exists. This causes it to be very tricky to trace where errors actually occurred.
Print the stack trace if available when an exception occurs in an event listener. I believe the stack trace just needs added to the following line. https://github.com/nestjs/event-emitter/blob/master/lib/event-subscribers.loader.ts#L187
No response
It is very difficult to track where an error originated without the stack trace.
2.0.0 > 2.0.1
The following unit test should pass for the new 2.0.1 version.
class FakeSuccessfulEvent {
constructor(readonly name: string) {}
}
class FakeFailedEvent {
constructor(readonly name: string) {}
}
@Injectable({ scope: Scope.DEFAULT })
class FakeService {
successCalled = false;
failCalled = false;
processed = false;
constructor(private readonly publisher: EventEmitter2) {}
async success(name = "fake"): Promise<void> {
await this.publisher.emitAsync(FakeSuccessfulEvent.name, new FakeSuccessfulEvent(name));
this.processed = true;
}
async fail(name = "fake"): Promise<void> {
await this.publisher.emitAsync(FakeFailedEvent.name, new FakeFailedEvent(name));
this.processed = true;
}
@OnEvent(FakeSuccessfulEvent.name)
async handleSuccess(_event: FakeSuccessfulEvent): Promise<void> {
this.successCalled = true;
}
@OnEvent(FakeFailedEvent.name)
async handleFail(event: FakeFailedEvent): Promise<void> {
this.failCalled = true;
throw new Error(`Failed to handle event ${event.name}`);
}
}
describe("EventEmitter", () => {
let app: INestApplicationContext;
let service: FakeService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [FakeService],
imports: [
EventEmitterModule.forRoot({
wildcard: true,
delimiter: ".",
verboseMemoryLeak: true,
}),
],
}).compile();
app = await module.init();
service = app.get(FakeService);
});
it("should successfully handle event", async () => {
await service.success();
expect(service.successCalled).toBe(true);
expect(service.failCalled).toBe(false);
expect(service.processed).toBe(true);
});
it("should fail when event isn't processed", async () => {
await expect(service.fail("failed")).rejects.toThrow("Failed to handle event failed");
expect(service.successCalled).toBe(false);
expect(service.failCalled).toBe(true);
expect(service.processed).toBe(false);
});
afterEach(async () => {
await app.close();
});
});
To pass both unit tests (like it is when using v2.0.0). If suppressing errors is the new behavior, maybe the right way to go could be creating a minor/major version (to avoid breaking changes in a patch)
No response
When I assemble aTestingModule
which does not resemble the full AppModule
, my event listeners don't get registered at the EventEmitter
With this code:
const module: TestingModule = await Test.createTestingModule({
imports: [EventEmitterModule.forRoot()],
providers: [RecipesService, EventEmitter2],
}).compile()
const app = await module.createNestApplication();
await app.init();
the events don't work,
If I initialize the complete AppModule like this:
const module: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
const app = await module.createNestApplication();
await app.init();
the events work fine.
this is my AppModule
:
@Module({
imports: [
EventEmitterModule.forRoot(),
RecipesModule,
]
})
export class AppModule {}
yarn test
Events should be emitted and listened to in both scenarios
1.4.1
8.4.7
18.10.0
No response
Build nestJS application with @nwrl/webpack using optimization: true
. It'll change name of EventEmitter
class name so DI container can't resolve this provider.
The root cause may be EventEmitter
is a function not a class.
This is config of TerserPlugin of @nwrl/webpack
new TerserPlugin({
parallel: true,
terserOptions: {
keep_classnames: true,
ecma: 2020,
safari10: true,
output: {
ascii_only: true,
comments: false,
webkit: true,
},
},
})
none
No response
DI container can resolve EventEmitter
provider
1.4.1
9.1.4
16.x
No response
I'm trying to catch all my events and log them all by name and payload:
This gives undefined:
@OnEvent("**")
public handleEverything(parentId: number): void {
// log all events by event name
// @ts-expect-error this.event
console.log(this.event)
}
This doesn't get called:
public constructor(private readonly eventEmitter: EventEmitter2) {
this.eventEmitter.on("**", function (value1, value2) {
// @ts-expect-error this.event
console.log(this.event)
})
}
Somehow have access to the event name.
No response
Avoid lots of boilerplate.
I would like to create durable request scoped event subscribers.
From what I saw in the documentation, request scoped event subscribers shouldn't be supported: https://docs.nestjs.com/techniques/events#:~:text=WARNING,be%20request%2Dscoped.
Looking at the code I do see support for it though through the registerRequestScopedListener
function in event-subscribers-loader.ts
Currently, the request scoped subscriber version generates a context id, instead of using the context id strategy.
I would like a durable version for the given loader. Meaning one that relies on the context id strategy instead of generating a context id automatically.
@Injectable({ scope: Scope.REQUEST, durable: true })
export class MyDurableInjectable() {
@OnEvent('my-event')
private async myEventHandler(event: any) {
...
}
}
My app uses durable providers, I would like to create event subscribers in those providers and have them remain durable.
The new version of event-emitter 2.0.1 suppress all errors by default, as described here: #932. As the thread was closed, I'd like to suggest a way to keep the old behavior, because we can't update to the new version, since getting the errors when a event is unprocessed is crucial for us.
It'd be nice if we can just include a property suppressErrors
in EventEmitterModule
or in OnEvent
so it will be possible to get errors from failed listeners. Without that, probably we'll have to stuck with the 2.0.0 version.
No response
The main goal is to get the erros thrown by the decorated OnEvent
methods. This option could be deactivated by default (since the default now is to suppress all errors), but with an option to use the last behavior (suppressErrors: false
), since that's very important for our projects.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
package.json
eventemitter2 6.4.9
@commitlint/cli 19.4.0
@commitlint/config-angular 19.3.0
@nestjs/common 10.4.1
@nestjs/core 10.4.1
@nestjs/platform-express 10.4.1
@nestjs/testing 10.4.1
@types/jest 29.5.12
@types/node 20.16.1
@typescript-eslint/eslint-plugin 8.2.0
@typescript-eslint/parser 8.2.0
eslint 8.57.0
eslint-config-prettier 9.1.0
eslint-plugin-import 2.29.1
husky 9.1.5
jest 29.7.0
lint-staged 15.2.9
prettier 3.3.3
reflect-metadata 0.2.2
release-it 17.6.0
rimraf 6.0.1
rxjs 7.8.1
ts-jest 29.2.5
typescript 5.5.4
@nestjs/common ^8.0.0 || ^9.0.0 || ^10.0.0
@nestjs/core ^8.0.0 || ^9.0.0 || ^10.0.0
When an event handler is registered
@OnEvent('event')
@OnEvent('event', {async: true})
@OnEvent('event', {async: true})
An unhandled exception in the event handler causes the app to exit
https://github.com/Babbiorsetto/nestjs-event-emitter-crash
npm i
npm start:dev
curl http://localhost:3000
I expect the exception/Promise rejection to be caught and the app to not crash
1.3.1
9.2.0
16.18.0
No response
We have an AppModule that includes many modules. This is a bad interaction between two in particular:
$extends
function to add client features.Strangely, when the factory function finishes adding the PrismaModule using the $extends
function, the EventEmitter
crashes with the following stack trace:
/Users/zack/code/learning/bug-repro/node_modules/reflect-metadata/Reflect.js:354
throw new TypeError();
^
TypeError:
at Reflect.getMetadata (/Users/zack/code/learning/bug-repro/node_modules/reflect-metadata/Reflect.js:354:23)
at Reflector.get (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/core/services/reflector.service.js:39:24)
at EventsMetadataAccessor.getEventHandlerMetadata (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/events-metadata.accessor.ts:13:37)
at EventSubscribersLoader.subscribeToEventIfListener (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/event-subscribers.loader.ts:76:29)
at /Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/event-subscribers.loader.ts:59:18
at MetadataScanner.scanFromPrototype (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/core/metadata-scanner.js:34:31)
at /Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/event-subscribers.loader.ts:55:30
at Array.forEach (<anonymous>)
at EventSubscribersLoader.loadEventListeners (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/event-subscribers.loader.ts:51:8)
at EventSubscribersLoader.onApplicationBootstrap (/Users/zack/code/learning/bug-repro/node_modules/@nestjs/event-emitter/lib/event-subscribers.loader.ts:39:10)
This appears to be because Prisma's $extend
function creates a Proxy
object. Event-emitter tries to get the prototype of the proxied object, which returns the underlying class, and then get its keys. It then tries to iterate these keys on the Proxy and assumes they will all be defined. However, Proxy
objects are free to redefine keys to be undefined or primitive values. When one of these is passed to reflect-metadata
, it then crashes with this type error.
https://github.com/zackdotcomputer/nest-event-bugreport
npm i
npm start:dev
Observe crash
The startup process should not crash. Instead, if the event-emitter receives a result in its iteration that is neither an object nor a function, it should not pass it on to reflect-metadata.
2.0.2
10.2.7
18.18.0
No response
[ ] Regression
[ ] Bug report
[x] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
I would like to have some kind of LoggingService
that is able to catch all events
that are emitted within my application. I correctly enabled wildcard
in my EventEmitterModule
on the top level.
Now i would like to catch all emitted events and store them within a database, like so:
@OnEvent('*')
async logToDatabase(event: any) {
console.log('logging event to database');
}
However, the *
wildcard does not catch events like user.created
, because respective event has 2 blocks (user
and created
). In order to catch such events, the wildcard argument should be *.*
.
However, if i use *.*
, i would not be able to catch an event like user.attached.team
or whatever.
Can we add a "real" wildcard that catches everything? Like, wouldn't it be better to have a *
and a ?
wildcard?
*
catches everything - does not really matter how many blocks will follow. *
will catch user.created
but also user.attached.teams
or shutdown
. In turn, user.*
will catch the user.created
and user.attached.teams
event.?
will only work for one particular block, so ?
will catch only events with one block length, like shutdown
, whereas user.?
will catch everything within user.
.Thank you very much for your reply and suggestions
[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Currently, an alternative implementation of EventEmitter2
cannot be provided
I don't know if this feature is an scope of this package, but I think this could provide more flexibility.
Common Interface:
interface EventEmitter {
emit(event: IEvent): boolean;
...
}
EventEmitter
as injection token:
abstract class EventEmitter {}
EventEmitter default implementation:
class EventEmitter2 implements EventEmitter {
...
}
Provide a custom implementation:
{
provide: EventEmitter2,
useValue: options.custom(options) || new EventEmitter2(options),
},
FYI: I try to use my custom implemetation providing it like
{
provide: EventEmitter2,
useValue: MyEventEmitter2(options),
},
at AppModule
but it means that listeners are not registered properly
In some cases, could be useful to use an alternative implementation of this class.
--
@kamilmysliwiec I wish know your thoughts in order to try an implementation 🙏🏻
no
Upgrade to use event emitter 3, or add an option for it
No response
Event emitter 3 is faster, not use nodeJs event.
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
There's a strange error with NestJs and its event emitter. I have an issue that if I remove an invokation of "emit" method in class A, then class B (which uses it) cannot be resolved Error: Nest can't resolve dependencies of the B (?, C)
=> A is unknown. When I add this line all works, when I comment it again everything fails.
This is strange, I know. Lack of declaration of method's invokation causes whole class to be undiscoverable by DI. Invokation doesn't take place (since class is not yet instatiated), but it's required for application to start.
When I change the event name it will also throw an error. So I need both invokation AND proper first string. Mystery to me.
I once had it my tests also (i.e. in one spec file). Test module compiled ok, but my test class couldn't be resolved.
I have lots of code in the same project that uses event emitter as both producers or consumers of events and I'm not experiencing similar issues.
Unfortunatelly I cannot provide a minimum repo, since this bug appears only in a specific case (but the same code fails on every machine) and I cannot give you the full code, because corporation would do some bad things to me.
I've used many minor versions of nest components, but always stick to major 6. Event emitter component was used in majors 0 and 1.
Currently I have:
"@nestjs-addons/in-memory-db": "^3.0.3",
"@nestjs/common": "^7.6.18",
"@nestjs/config": "^1.0.0",
"@nestjs/core": "^7.6.18",
"@nestjs/event-emitter": "1.0.0",
"@nestjs/platform-express": "^7.6.18",
"@nestjs/schedule": "^1.0.0",
"@nestjs/swagger": "^4.8.2",
@OnEvent
decorator
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Here i have simple class with subscribers and emitters.
@WebSocketGateway({path: '/api/ws', serveClient: false})
export class WebsocketGateway implements OnGatewayConnection<Socket>, OnGatewayDisconnect<Socket> {
private online = 0
@WebSocketServer()
public readonly server: Server
@Inject()
private readonly eventEmitter2!: EventEmitter2
async handleConnection(client: Socket, ...args: any[]) {
this.online++
this.eventEmitter2.emit(WsEvent.ConnectClient, client)
}
async handleDisconnect(client: Socket) {
this.online--
this.eventEmitter2.emit(WsEvent.DisconnectClient, client)
}
@OnEvent([WsEvent.ConnectClient, WsEvent.DisconnectClient])
public emitOnlineUsers() {
this.emit('online', this.online)
}
public emit(event: string, ...args: Array<any>) {
this.server.emit(event, ...args)
}
}
I want to execute emitOnlineUsers()
when one of ConnectClient
or DisconnectClient
fires. But i got nothing.
Array subscription via @OnEvent
not working.
Here another super simple examle.
@Module({
imports: [
EventEmitterModule.forRoot({}),
],
})
export class AppModule implements OnApplicationBootstrap {
@Inject()
private readonly eventEmitter2!: EventEmitter2
onApplicationBootstrap() {
setTimeout(() => this.eventEmitter2.emit('test1', 'test1'), 500)
setTimeout(() => this.eventEmitter2.emit('test2', 'test2'), 700)
}
@OnEvent('test1')
private singleSubscription(data: string) {
console.log('singleSubscription', data)
}
@OnEvent(['test2'])
private arraySubscription1(data: string) {
console.log('arraySubscription1', data)
}
@OnEvent(['test1', 'test2'])
private darraySubscription2(data: string) {
console.log('arraySubscription2', data)
}
}
Expected:
singleSubscription test1
arraySubscription2 test1
arraySubscription1 test2
arraySubscription2 test2
Got:
singleSubscription test1
arraySubscription1 test2
I think you should modify /lib/event-subscribers.loader.ts#58
to handle array in event
variable.
private subscribeToEventIfListener(
instance: Record<string, any>,
methodKey: string,
) {
const eventListenerMetadata = this.metadataAccessor.getEventHandlerMetadata(
instance[methodKey],
);
if (!eventListenerMetadata) {
return;
}
const { event, options } = eventListenerMetadata;
if (Array.isArray(event)) {
for (const e of event) {
this.eventEmitter.on(
e,
(...args: unknown[]) => instance[methodKey].call(instance, ...args),
options,
);
}
} else {
this.eventEmitter.on(
event,
(...args: unknown[]) => instance[methodKey].call(instance, ...args),
options,
);
}
}
win 10
node v15.5.0
@nestjs/core 7.6.1
@nestjs/event-emitter 0.0.2
Would be a great addition to have a decorator to emit an event, for example in a controller.
In staart/api, I have a decorator @AuditLog
which is called like so:
@Controller('domains')
export class DomainController {
constructor() {}
@Post()
@AuditLog('create-domain')
async create() {
return { ok: true }
}
}
Similarly, we could do:
@Controller('domains')
export class DomainController {
constructor() {}
@Post()
@EmitEvent('domain-created', new CreateDomainEvent())
async create(): Promise<{ ok: boolean }> {
return { ok: true }
}
}
In this example, the event will be dispatched in the function doesn't throw an error. With this use case, I can replace my audit logger with an event emitter! 😄
Doing something like should work:
@Injectable()
class Service {
@OnEvent('event.one')
@OnEvent('event.two')
private twoEventsOnOneMethod(data: any) {
// ...
}
}
As it should roughly translate to two .on
calls on event emitter2:
eventEmitter2.on('event.one', () => service.twoEventsOnOneMethod)
eventEmitter2.on('event.two', () => service.twoEventsOnOneMethod)
But it doesn not. It only uses one of the OnEvent
instead of all.
https://github.com/mentos1386/nestjs-events-on-event-issue
npm i
npm start
curl http://localhost:3000/
In the logs, we would expect to see "hello from event one" and "hello from event two'. But only one will be present.
1.1.1
8.4.7
v16.15.1
Reference to issue that touches this topic #52 (comment)
Simply add a module that is included in a npm package to the app that contains a service that consumes the EventEmitter2
service.
This lead to this error
Error: Nest can't resolve dependencies of the PackageService (?). Please make sure that the argument EventEmitter at index [0] is available in the PackageModule context.
Instead, when I import the module directly it works as expected.
https://github.com/twilker/nest-event-bug-app
main
branch to see wrong behaviorworking
branch to see expected bahaviorIt should be possible to use it in npm packages as the purpose is to separate modules better by loose coupling. In order to facilitate the separation it is obvious to wish to split the nest app into multiple separately versioned npm packages and consume it in a single nest app.
1.4.2
9.4.3
18.12.1
No response
Hello,
I would like to understand how does it differs from the EventBus of the @nestjs/cqrs package ?
As I'm currently starting a new project with CQRS, I wanted to know if it was better using the @nestjs/event-emitter package with @nestjs/cqrs or does it didn't adds added value. What about stability/scalability/... ?
I understand the underlying system changes.
Thanks,
Passing an array to OnEvent
does not work, and that might be intended behavior but is not mentioned anywhere in the docs.
That being said there is currently no way to handle multiple events using this module. Multiple @OnEvent
would overwrite previous metadata.
The bug report #596 was just closed stating that indeed this does not work. (While using wildcards is viable in some cases, it is not in others)
Is this intentional and there is an important reason why this behavior is made impossible? Fixing this should be very simple, by simply changing the way metadata is stored to hold an array of settings, and on initialization walk the array. For less confusion in decorators and backwards compatibility, @OnEvents
could be added, that would explicitly suggest that the array input will be treated as separate events. Should I open a PR for adding this functionality?
This is something that can be done in an hours time or less, and currently one needs to resort to:
@Injectable()
export class GenericListener implements OnApplicationBootstrap {
constructor(
private readonly eventEmitter: EventEmitter2,
) {}
onApplicationBootstrap() {
const callback = (event: EventData) =>
this.handleGenericEvent(event);
const events = [
EVENT_ONE,
EVENT_TWO
EVENT_THREE,
];
for (const event of events) {
this.eventEmitter.on(event, callback);
}
}
provided in issue
No response
@OnEvents([
EVENT_ONE,
EVENT_TWO,
EVENT_THREE,
])
async handleGenericEvent(event: EventData) {
or
@OnEvent(EVENT_ONE)
@OnEvent(EVENT_TWO)
@OnEvent(EVENT_THREE)
async handleGenericEvent(event: EventData) {
work as one would expect
master
No response
No response
No response
[ ] Regression
[X] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Injected EventEmitter2 in constructor is undefined when used in a child module class
It should be a EventEmitter2 instance everywhere (it should be global)
Just inject EventEmitter2 in a service of a child module and do a console.log of it
Nest version: 7.5.2
[ ] Regression
[ ] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
It's unclear how to emit an event from inside an entity. Or if this is possible at all. For example: I have a product and I want to decrement the inventory when someone buys it. product.adjustInventory(-1)
. I would like that function to emit an event incrementInventory
, { oldAmount, newAmount, productId }. Is this possible? I want the product to be responsible for itself.
[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Currently, it is not possible to add listeners to providers that do not have a static dependency tree:
It would be nice to be able to register event listeners in a non-static provider. Or make it explicit in docs that it won't work in providers with dynamic dependency trees.
OnEvent
, as docs suggest, subscribing to a event.I was trying to create a handler method in one of my resolvers that have a dataloader being injected into it. Due to the fact that dataloaders must be request scoped, this provider had a dynamic dependency tree, and the event handler was not being registered due to the logic mentioned above in the Current Behavior
section.
I don't know if this was a expected behavior or a bug, so I marked it as a feature request, but I think this kind of hinders a little bit the usefulness of this package in a GraphQL scenario, as most resolvers will have a loader injected onto them making its dependency tree dynamic.
Nest version: 7.5.4
nest event emitter version: 0.0.2
For Tooling issues:
- Node version: 12.18.4
- Platform: Windows
The Following Listener Triggered 2 Time on a single Emit
@OnEvent('order.failed')
handleOrderFailed(payload: any) {
console.log(payload);
}
Where as the below works currectly,
@Injectable()
export class GlobalSubscriber {
constructor(private eventEmitter: EventEmitter2) {
this.eventEmitter.on('**', (payload) => {
console.log(payload);
});
}
}
Seems BUG, please Verify
i chnaged OS, also run same in server ubuntu, but still it trigger 2 time, i getting console.log 2 time in console.
https://github.com/nestjs/nest/tree/master/sample/30-event-emitter
npm i
The Event Subscriber should trigger only 1 time, and not Multiple time
1.1.1
8.4.4
14+
No response
When defining providers with useExisting, any OnEvent handlers get registered multiple times with event-emitter
With the repo below, this is the console.log output:
Sending Event
Event Received [
'4e9f9082-5ad0-4c6d-84d1-8bd036a746e7',
'897c020e-7f77-4b6d-b298-579da9025cfe'
]
Event Received [
'4e9f9082-5ad0-4c6d-84d1-8bd036a746e7',
'897c020e-7f77-4b6d-b298-579da9025cfe'
]
This shows that the same event was received by the same object twice.
According to the documentation useExisting is only supposed to create an alias, I do not expect the service to start doing duplicate work.
https://github.com/matt1097/nestjs-event-issue
The following output is what is expected:
Sending Event
Event Received [
'4e9f9082-5ad0-4c6d-84d1-8bd036a746e7',
'897c020e-7f77-4b6d-b298-579da9025cfe'
]
Basically that the event was handled only once
1.3.0
9.0.8
14.19.3
I can get the expected behavior by modifying the forEach loop here: https://github.com/nestjs/event-emitter/blob/master/lib/event-subscribers.loader.ts#L49
if I add:
if (wrapper.isAlias) {
return;
}
at the beginning of the foreach loop, it will skip registering the event handler a second time and I will get my expected behavior
[ ] Regression
[x] Bug report
[ ] Feature request
[x] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
When trying to use the event emitter inside a TestingModule the OnEvent decorator doesn't add a listener.
OnEvent should add the listener.
describe('LoremService', () => {
let service: LoremService;
let eventEmitter: EventEmitter2;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [EventEmitterModule.forRoot()],
providers: [LoremService],
}).compile();
service = module.get(LoremService);
eventEmitter = module.get(EventEmitter2);
});
it('should perform some operation when user created', async () => {
jest.spyOn(service, 'someOperation');
await eventEmitter.emitAsync('user.created');
// eventEmitter.hasListeners() returns false
expect(service.someOperation).toHaveBeenCalled(); // test fails
});
});
@Injectable()
export class LoremService {
@OnEvent('user.created')
async someOperation(): Promise<any> {
// ...
}
}
It should be possible to test that some logic is called when a certain event is triggered.
The documentation should be extended to describe how to write unit tests with the event emitter.
Nest version: 7.5.5
For Tooling issues:
- Node version: v12.18.0
- Platform: Linux
Others:
Creating a standalone application calls the event listener multiple times.
https://github.com/boy672820/nest-event/blob/main/src/main.ts#L6
No response
const app = await NestFactory.create(AppModule);
const standalone = await NestFactory.createApplicationContext(AppModule);
const bupService = standalone.get(BupService);
bupService.subscribeTransfer();
^1.0.0
No response
No response
No response
There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.
Error type: Cannot find preset's package (github>whitesource/merge-confidence:beta)
In NestJS applications using the @OnEvent
decorator, there's a risk of losing events emitted before the full completion of the application's bootstrap process. Specifically:
onApplicationBootstrap
lifecycle hook, including those from module constructors or onModuleInit
, may go unnoticed because EventSubscribersLoader
might not have completed setting up the listeners.onApplicationBootstrap
hook isn't guaranteed to be caught, as there's no assurance that all listeners are ready at this point.This early emission problem threatens the reliability of applications relying on events for tasks like state initialization, startup procedures, or module communication. Ensuring all events are captured is crucial for maintaining consistent application behavior and avoiding missed operations or errors.
App Start
├─ Module Initialization
│ └─ 🚨 Potential Early Event Emission (Risk)
│
├─ Application Bootstrap
│ ├─ 🚨 Another Potential Early Event Emission (Risk)
│ └─ EventSubscribersLoader (onApplicationBootstrap)
│ └─ Event Listeners Setup (👂 Listening Starts)
The solution involves several potential strategies:
The motivation is to ensure reliable and consistent event handling in NestJS applications. Events are crucial for asynchronous tasks and inter-service communication. By ensuring that no events are lost due to timing issues related to application initialization, we improve the robustness, predictability, and reliability of applications. This change will benefit all NestJS users who rely on event-driven architecture, especially those building complex or modular applications where timing and order of initialization are critical.
I have not managed to run the events in the repl. I try to trigger them in the repl by using the eventEmitter instance from the dependency injection mechanism but failed to do so. I created a new repo to test this and turns out there's something wrong. Here's a detailed overview
@nestjs/core v9
):I have EventEmitter.forRoot({ global: true }) imported in app.module.ts
only
I added this method to the app.service.ts which is directly declared as a provider in app.module.ts only
in the repl I try
get(EventEmitter2)
I get an error, the token doesn't exist
get(EventEmitter)
works but the event emitter is empty the result of that get is this
EventEmitter {
_events: {},
_newListener: false,
_removeListener: false,
verboseMemoryLeak: false
}
I set the newListener option to true
and tried this again to check if this one would reflect the change and it didn't work
I got creative and tried this:
get(EventSubscribersLoader).eventEmitter
and it works ! I recognize all my events registered on my app.
I try emitting an event and it works BUT the eventHandler is ran twice ? I blame it on my self, maybe I am wrong because the handler was in a dynamic module
I add a method to the app.service.ts
which is only provided in app.module.ts
@Injectable()
export class AppService {
constructor(eventEmitter: EventEmitter2) {
console.log('AppModuleInit');
console.log(eventEmitter);
}
getHello(): string {
return 'Hello World!';
}
@OnEvent('testevent', { async: true, promisify: true })
async test() {
console.log('test text');
}
}
I emit testevent
like this:
await get(EventSubscribersLoader).eventEmitter.emitAsync('testevent')
I get this
test text
test text
[ undefined, undefined ]
I reproduced this in a different repo linked below, same error
@nestjs/core v10
):App.service.ts
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
@Injectable()
export class AppService {
@OnEvent('testEvent', { promisify: true, async: true })
getHello(): string {
return 'Hello World!';
}
}
getting the eventEmitter directly works for some reason
> get(EventEmitter)
EventEmitter {
_events: { testEvent: [ [Function], [Function] ] },
_newListener: false,
_removeListener: false,
verboseMemoryLeak: false,
_conf: { global: true }
}
BUT again !
> await get(EventEmitter).emitAsync('testEvent')
[ 'Hello World!', 'Hello World!' ]
https://github.com/adelbke/event-emitter-duplicate-events-reproduction
No response
Every event emitted once should trigger listeners once and once only !
"^2.0.2" in my codebase "^2.0.4" in the reproduction repo
"^9.0.11" in my codebase "^10.0.0" in the reproduction repo
v16.14.2 tested also with node v18.12.0
No response
According to interface and docs decorator should accept string | symbol | Array<string | symbol>
. It works fine if string provided for example @OnEvent(some.event)
. But it does not trigger if array was provided like : @OnEvent(['some.event1', 'some.event2'])
.
https://codesandbox.io/s/happy-ishizaka-d0gjsf
check terminal view
It should trigger function that was decorated with array
@nestjs/common
@nestjs/core
@nestjs/microservices
@nestjs/platform-express
@nestjs/platform-fastify
@nestjs/platform-socket.io
@nestjs/platform-ws
@nestjs/testing
@nestjs/websockets
@nestjs/event-emitter
No response
{
"name": "nest-typescript-starter",
"private": true,
"version": "1.0.0",
"description": "Nest TypeScript starter repository",
"license": "MIT",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/event-emitter": "1.3.1",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "^28.1.4",
"@types/node": "^18.0.3",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.30.5",
"@typescript-eslint/parser": "^5.30.5",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^28.1.2",
"prettier": "^2.7.1",
"source-map-support": "^0.5.21",
"supertest": "^6.2.4",
"ts-jest": "^28.0.5",
"ts-loader": "^9.3.1",
"ts-node": "^10.8.2",
"tsconfig-paths": "^4.0.0",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": ["**/*.(t|j)s"],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
No response
No response
Events are not emitted when I use "onModuleInit" for testing purposes
@Injectable()
export class AppService implements OnModuleInit {
constructor(private eventEmitter: EventEmitter2) {}
onModuleInit() {
this.getHello();
}
getHello(): string {
const message = 'Emitting event in AppService->getHello()';
console.log('get hello was trigged!');
this.eventEmitter.emit('message', message);
console.log(this.eventEmitter.listeners());
return message;
}
@OnEvent('message')
async onMessageReceived(payload: string) {
console.log('showing payload', payload);
}
}
The method "getHello" emits an event and this event is logged in the terminal. I am using "onModuleInit" for testing purposes and to check if everything is working but the event is not emitted when I use "onModuleInit". If I try to do a REST request to localhost:3000
everything works well!
https://github.com/felinto-dev/nestjs-event-emitter-issue
src/app.service.ts
to play around and debug this issueThe event should be emitted no matter whether I use onModuleInit
or send a REST API request.
1
8
16
No response
I would like to be able to use NestJS ExceptionFilters
with event listeners the same way they can be used on controllers with rest endpoints.
I would like to be able to use exception filters like the following on a controller and have it also filter exceptions that were thrown in the Event Emitter Listeners.
@Catch()
export class MyEventExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// Handle error
}
}
@Controller()
@UseFilters(MyEventExceptionFilter)
export class MySubscriptionController {
@OnEvent("MY_EVENT")
onEvent() {
// This should be able to be filtered
throw new Error('')
}
}
No response
It would be nice to be able to catch certain things happening more cleanly using this approach and be more consistent the way Nestjs handles rest controller errors.
Injecting object with null prototype into provider throw errors.
Injecting object don't throwing errors, when use with @nestjs/event-emmiter.
I understand that eventEmitter scans all modules and providers imported into AppModule
As a config file we use an ini file, and for parsing we use a simple module "ini-parser", but ini.parse return object with null prototype.
The error is very subtle. An exception was thrown when called
EventEmmiterModule.forRoot()
The fact that the problem is in the "Object null prototype", I realized only when I started to get into node_modules and use console.log there.
https://stackblitz.com/edit/nestjs-typescript-starter-uizvtp?file=src/app.module.ts
Object injection must not throw an exception
I write possible solution this problem
let config = ini.parse(data as string) as Config;
// Null prorotype bug bypass
const configString = JSON.stringify(config);
config = JSON.parse(configString) as Config;
return config;
@nestjs/common
@nestjs/core
@nestjs/microservices
@nestjs/platform-express
@nestjs/platform-fastify
@nestjs/platform-socket.io
@nestjs/platform-ws
@nestjs/testing
@nestjs/websockets
@nestjs/event-emmiter
8.4.3
{
"name": "frontend-server",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"migrate": "ts-node src/helpers/migrations/migrations.ts",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"scripts:create-test-license": "ts-node scripts/create-test-license.ts"
},
"dependencies": {
"@nestjs/bull": "^0.5.4",
"@nestjs/common": "^8.4.3",
"@nestjs/core": "^8.4.3",
"@nestjs/event-emitter": "^1.1.0",
"@nestjs/platform-fastify": "^8.4.3",
"@nestjs/schedule": "^1.0.2",
"@nestjs/sequelize": "^8.0.0",
"@types/pg": "^8.6.5",
"bcryptjs": "^2.4.3",
"bull": "^4.8.1",
"date-fns": "^2.28.0",
"fastify-secure-session": "^3.0.0",
"ini": "^2.0.0",
},
"devDependencies": {
"@nestjs/cli": "^8.2.4",
"@nestjs/schematics": "^8.0.8",
"@nestjs/testing": "^8.4.3",
"@types/bcryptjs": "^2.4.2",
"@types/bull": "^3.15.8",
"@types/cron": "^1.7.3",
"@types/express": "^4.17.13",
"@types/ini": "^1.3.31",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.180",
"@types/node": "^16.11.24",
"@types/nodemailer": "^6.4.4",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.5.1",
"memfs": "^3.4.1",
"prettier": "^2.6.1",
"source-map-support": "^0.5.21",
"supertest": "^6.2.2",
"ts-jest": "^27.1.4",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"tsconfig-paths": "^3.14.1",
"type-fest": "^2.12.1",
"typescript": "^4.6.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"moduleNameMapper": {
"^src/(.*)": "<rootDir>/$1"
}
}
}
17.7.1
/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/core/metadata-scanner.js:23
yield* (0, iterare_1.iterate)(Object.getOwnPropertyNames(prototype))
TypeError: Cannot convert undefined or null to object
at Function.getOwnPropertyNames (<anonymous>)
at MetadataScanner.getAllFilteredMethodNames (/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/core/metadata-scanner.js:23:50)
at getAllFilteredMethodNames.next (<anonymous>)
at new Set (<anonymous>)
at MetadataScanner.scanFromPrototype (/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/core/metadata-scanner.js:8:29)
at /home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/event-emitter/dist/event-subscribers.loader.js:39:34
at Array.forEach (<anonymous>)
at EventSubscribersLoader.loadEventListeners (/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/event-emitter/dist/event-subscribers.loader.js:36:14)
at EventSubscribersLoader.onApplicationBootstrap (/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/event-emitter/dist/event-subscribers.loader.js:25:14)
at MapIterator.iteratee (/home/almaz/omega-project-backend/frontend-server/node_modules/@nestjs/core/hooks/on-app-bootstrap.hook.js:22:43)
[ ] Regression
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
Using the library with Yarn 2 PnP mode causes an error during run:
Error: @nestjs/event-emitter tried to access @nestjs/core, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.
Required package: @nestjs/core (via "@nestjs/core")
Should run successfully.
nest new
yarn set version berry
)@nestjs/event-emitter
to a blank project as per normal.yarn start:dev
So that it works with Yarn 2 PnP.
Nest version: 7.6.17
For Tooling issues:
- Node version: 15.9
- Platform: Linux
Others:
I'd like to have some control over the ordering of event listeners. My general feature request is to provide an option such that I can ensure a given listener registered with the OnEvent
decorator is fired before another listener.
I'd like the OnEvent
decorator options to support an interface similar to:
type OnEventOptions = OnOptions & { prependListener?: boolean }
prependListener
will default to false
. In EventSubscribersLoader
, the new config value will be respected and register the decorated subscriber via:
on
when prependListener
is false
prependListener
when prependListener
is true
eventemitter2 ref: https://github.com/EventEmitter2/EventEmitter2#emitterprependlistenerevent-listener-options
This feature will be backwards-compatible as the new configuration option is optional and defaults to false
.
I'd like to ensure that a given decorator event subscriber is always triggered first.
When using a custom event decorator an error is thrown: eventListenerMetadatas is not iterable
. After digging it is clear that the latest release 1.4.1
and specifically this PR #657 causes it.
https://github.com/davidzwa/nest-event-bug/actions/runs/4292223946/jobs/7478397448
See MR repo workflow for the error https://github.com/davidzwa/nest-event-bug/actions/runs/4292223946/jobs/7478397448
The array type should either be enforced with the return type of the decorator (now using generic MethodDecorator
) or preferably it should rather be backwards compatible and allow non-array event metadata to work without failure.
Undesirable alternative:
The error eventListenerMetadatas is not iterable
should be prevented and clarified.
Takeaway: the return type at
is not enforced at build time.1.4.1
^9.0.0
18.14.2
No response
The code taken from the official guide doesn't seem to work for me.. I've got a config for eventing module
export const globalEventingConfig : EventEmitterModuleOptions = {
global: true,
wildcard: true,
maxListeners: 32
}
Which is used in the module definition:
@Module({
imports: [
EventEmitterModule.forRoot(globalEventingConfig),
],
providers: [
EventEmitterService
]
})
export class AlPacinoModule { }
Yes, I still have memory leak warnings at startup: MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 ready listeners added to [Client]. Use emitter.setMaxListeners() to increase limit
What am I doing wrong? Thanks a mil!
nest start
No warning is shown in logs
1.3.1
8.4.7
16.15.0
No response
OnEvent has problem with server stops.
import { applyDecorators, Logger } from '@nestjs/common';
import { OnEvent, OnEventType } from '@nestjs/event-emitter';
import { OnEventOptions } from '@nestjs/event-emitter/dist/interfaces';
function _OnSafeEvent() {
return function (target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
const metaKeys = Reflect.getOwnMetadataKeys(descriptor.value);
const metas = metaKeys.map((key) => [key, Reflect.getMetadata(key, descriptor.value)]);
descriptor.value = async function (...args: any[]) {
try {
await originalMethod.call(this, ...args);
} catch (err) {
Logger.error(err, err.stack, 'OnSafeEvent');
}
};
metas.forEach(([k, v]) => Reflect.defineMetadata(k, v, descriptor.value));
};
}
export function OnSafeEvent(event: OnEventType, options?: OnEventOptions | undefined) {
return applyDecorators(OnEvent(event, options), _OnSafeEvent());
}
No response
do not stop server when the exception not catched in OnEvent
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.