DEV Community

Cover image for Understanding Node.js Event Emitters
Thomas Pegler
Thomas Pegler

Posted on

Understanding Node.js Event Emitters

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 );
    }
}
Enter fullscreen mode Exit fullscreen mode

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 );
})
Enter fullscreen mode Exit fullscreen mode

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.
})
Enter fullscreen mode Exit fullscreen mode

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.
})
Enter fullscreen mode Exit fullscreen mode

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


Header by Artem Bryzgalov on Unsplash

Top comments (0)