Understanding Node.js Event Emitters
Thomas Pegler
Posted on August 21, 2023
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
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.
// 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.
// 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.
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:
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 Artem Bryzgalov on Unsplash
Posted on August 21, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.