Understanding Node.js Event Emitters
Tags: typescript, node, beginners, tutorial
Published: 2023-08-21 12:00:00

By now you've probably heard of Event Driven architecture, or maybe you haven't but you've wondered if there was a good way of running some arbitrary piece of code when a Mongo document was created. Well, you're in luck! There absolutely is and it's a fairly large part of Node.js itself: `EventEmitter`.

### How do?

Let's say you have a Mongo database, you're connecting to it through Mongoose and you have a document for these scheduled tasks that you want to run ever X minutes/hours/days/weeks. You're all set up, you've installed [`node-schedule`](https://www.npmjs.com/package/node-schedule) like a good developer but now you want to run some checks/updates _after_ any changes are made to the documents without bloating your model file.

```typescript
// common/events/mongo.ts
import { Document } from 'mongoose';
import events from 'node:events';

export class MongoEventEmitter extends events.EventEmitter {
    emitUpdated( modelData: Document ): void {
        this.emit( 'updated', modelData );
    };

    emitDeleted( modelData: Document ): void {
        this.emit( 'deleted', modelData );
    }

    emitCreated( modelData: Document ): void {
        this.emit( 'created', modelData );
    }
}
```

And then you can use this to create event emitters for each mongoose model that you create.

```typescript
// models/ScheduledTask.ts

import mongoose, { Document } from 'mongoose';
import events from 'node:events';

interface IScheduledTask {
    name: string;
    process: string;
    schedule: string;
    enabled: boolean;
    last_successful_run: number;
}

export const scheduledTaskSchema = new mongoose.Schema<IScheduledTask>(
    {
        name: { type: String, required: true},
        process: { type: String, required: true },
        schedule: { type: String, required: true },
        enabled: { type: Boolean, required: true },
        last_successful_run: { type: Number },
    },
    {
        collection: 'scheduled_tasks',
    }
);

export const ScheduledTask = mongoose.model<IScheduledTask>(
    'scheduled_tasks',
    scheduledTaskSchema
);

export const scheduledTaskEmitter = new MongoEventEmitter();

scheduledTaskSchema.post( 'save', ( base: Document ) => {
    scheduledTaskEmitter.emitCreated( base );
})
```

The next step is to implement a listener somewhere and do whatever you wanted to do with it.

```typescript
scheduledTaskEmitter.on( 'created', ( scheduledTask: Document ) => {
    console.log( 'created', scheduledTask);
    // Do something with the document.
})
```

One important thing to note is that while you can register as many listeners as you want for an event, they will be executed in the order they are registered (i.e. the order they appear in the code).

So, this:

```typescript
scheduledTaskEmitter.on( 'created', ( scheduledTask: Document ) => {
    console.log( 'Hey, someone created a scheduled task!', scheduledTask);
    // Do something with the document.
})

scheduledTaskEmitter.on( 'created', ( scheduledTask: Document ) => {
    console.log( `${scheduledTask.name}` );
    // Do something with the document.
})
```

Will cause the "Hey" line to be output before the `scheduledTask.name`. 

---

Header by <a href="https://unsplash.com/@abrizgalov?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Artem Bryzgalov</a> on <a href="https://unsplash.com/photos/r4qdM8iD5WY?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>