How to Create an Event Module

In this guide, you’ll learn how to create an Event Module.

1. Create Module Directory#

Start by creating a new directory for your module. For example, src/modules/my-event.


2. Create the Event Service#

Create the file src/modules/my-event/service.ts that holds the implementation of the event service.

The Event Module's main service must extend the AbstractEventBusModuleService class imported from @medusajs/framework/utils:

src/modules/my-event/service.ts
1import { AbstractEventBusModuleService } from "@medusajs/framework/utils"2import { Message } from "@medusajs/types"3
4class MyEventService extends AbstractEventBusModuleService {5  async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {6    throw new Error("Method not implemented.")7  }8  async releaseGroupedEvents(eventGroupId: string): Promise<void> {9    throw new Error("Method not implemented.")10  }11  async clearGroupedEvents(eventGroupId: string): Promise<void> {12    throw new Error("Method not implemented.")13  }14}15
16export default MyEventService

The service implements the required methods based on the desired publish/subscribe logic.

eventToSubscribersMap_ Property#

The AbstractEventBusModuleService has a field eventToSubscribersMap_, which is a JavaScript Map. The map's keys are the event names, whereas the value of each key is an array of subscribed handler functions.

In your custom implementation, you can use this property to manage the subscribed handler functions:

Code
1const eventSubscribers = 2  this.eventToSubscribersMap_.get(eventName) || []

emit Method#

The emit method is used to push an event from the Medusa application into your messaging system. The subscribers to that event would then pick up the message and execute their asynchronous tasks.

An example implementation:

src/modules/my-event/service.ts
1class MyEventService extends AbstractEventBusModuleService {2  async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {3    const events = Array.isArray(data) ? data : [data]4
5    for (const event of events) {6      console.log(`Received the event ${event.name} with data ${event.data}`)7
8      // TODO push the event somewhere9    }10  }11  // ...12}

The emit method receives the following parameters:

dataobject or array of objects
The emitted event(s).

releaseGroupedEvents Method#

Grouped events are useful when you have distributed transactions where you need to explicitly group, release, and clear events upon lifecycle transaction events.

If your Event Module supports grouped events, this method is used to emit all events in a group, then clear that group.

For example:

src/modules/my-event/service.ts
1class MyEventService extends AbstractEventBusModuleService {2  protected groupedEventsMap_: Map<string, Message[]>3
4  constructor() {5    // @ts-ignore6    super(...arguments)7
8    this.groupedEventsMap_ = new Map()9  }10
11  async releaseGroupedEvents(eventGroupId: string): Promise<void> {12    const groupedEvents = this.groupedEventsMap_.get(eventGroupId) || []13
14    for (const event of groupedEvents) {15      const { options, ...eventBody } = event16
17      // TODO emit event18    }19
20    await this.clearGroupedEvents(eventGroupId)21  }22
23  // ...24}

The releaseGroupedEvents receives the group ID as a parameter.

In the example above, you add a groupedEventsMap_ property to store grouped events. Then, in the method, you emit the events in the group, then clear the grouped events using the clearGroupedEvents which you'll learn about next.

To add events to the grouped events map, you can do it in the emit method:

src/modules/my-event/service.ts
1class MyEventService extends AbstractEventBusModuleService {2  // ...3  async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {4    const events = Array.isArray(data) ? data : [data]5
6    for (const event of events) {7      console.log(`Received the event ${event.name} with data ${event.data}`)8
9      if (event.metadata.eventGroupId) {10        const groupedEvents = this.groupedEventsMap_.get(11          event.metadata.eventGroupId12        ) || []13
14        groupedEvents.push(event)15
16        this.groupedEventsMap_.set(event.metadata.eventGroupId, groupedEvents)17        continue18      }19
20      // TODO push the event somewhere21    }22  }23}

clearGroupedEvents Method#

If your Event Module supports grouped events, this method is used to remove the events of a group.

For example:

src/modules/my-event/service.ts
1class MyEventService extends AbstractEventBusModuleService {2  // from previous section3  protected groupedEventsMap_: Map<string, Message[]>4
5  async clearGroupedEvents(eventGroupId: string): Promise<void> {6    this.groupedEventsMap_.delete(eventGroupId)7  }8
9  // ...10}

The method accepts the group's name as a parameter.

In the method, you delete the group from the groupedEventsMap_ property (added in the previous section), deleting the stored events of it as well.


3. Create Module Definition File#

Create the file src/modules/my-event/index.ts with the following content:

src/modules/my-event/index.ts
1import MyEventService from "./service"2import { Module } from "@medusajs/framework/utils"3
4export default Module("my-event", {5  service: MyEventService,6})

This exports the module's definition, indicating that the MyEventService is the main service of the module.


4. Use Module#

To use your Event Module, add it to the modules object exported as part of the configurations in medusa-config.ts. An Event Module is added under the eventBus key.

For example:

medusa-config.ts
1import { Modules } from "@medusajs/framework/utils"2
3// ...4
5module.exports = defineConfig({6  // ...7  modules: [8    {9      resolve: "./src/modules/my-event",10      options: { 11        // any options12      },13    },14  ],15})
Was this page helpful?
Edit this page